Compare commits
41 Commits
ce7d2066b3
...
4409aef9e2
| Author | SHA1 | Date | |
|---|---|---|---|
| 4409aef9e2 | |||
| a9389070bf | |||
| dc0de05f3b | |||
| a39396e1d1 | |||
| 6ad151c82d | |||
| 723fa38497 | |||
| bf34ca9937 | |||
| 7f6a5f1d34 | |||
| 485aa7a46f | |||
| a54e734d46 | |||
| fbedb63e3a | |||
| 2b2d449044 | |||
| b1e048e637 | |||
| dfaa4de44b | |||
| 5581d00b15 | |||
| d4e46d5ddb | |||
| 96c7eece9b | |||
| 62c136d8d4 | |||
| b6872e2a3f | |||
| 8be9516338 | |||
| 848f0a215e | |||
| 750968d403 | |||
| 2e23d8808c | |||
| 4b65e389bf | |||
| 80c9d014da | |||
| c45f90ad82 | |||
| 0842a0b357 | |||
| 9870e738e7 | |||
| 974066b8cf | |||
| 12f836d427 | |||
| 2024dd3b6d | |||
| 54356812d6 | |||
| 7a54d0c4c8 | |||
| 59ffca2155 | |||
| 45602d0da7 | |||
| 25ca644626 | |||
| 3b76e5fafa | |||
| a67a2ec7ea | |||
| f71220fa80 | |||
| f957d36e14 | |||
| 4cf063dce0 |
|
|
@ -2,19 +2,21 @@
|
|||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
"dont_have_account": "መለያ የለዎትም? ይመዝገቡ",
|
||||
"dont_have_account": "መለያ የለዎትም?",
|
||||
"email": "ኢሜይል",
|
||||
"password": "የይለፍ ቃል",
|
||||
"forgot_password": "የይለፍ ቃል ረሱ?",
|
||||
"cont": "ቀጥል",
|
||||
"register": "ይመዝገቡ",
|
||||
"login_with_google": "በጉግል ይግቡ",
|
||||
"login_with_apple": "በአፕል ይግቡ",
|
||||
"or": "ወይም",
|
||||
"login_with_phone": "በስልክ ቁጥር ይግቡ",
|
||||
"create_account": "አዲስ መለያ ይፍጠሩ",
|
||||
"already_have_account": "መለያ አለዎት?",
|
||||
"login": " ይግቡ ",
|
||||
"register_with_google": "በጉግል ይመዝገቡ",
|
||||
"register_with_apple": "በአፕል ይመዝገቡ",
|
||||
"register_with_phone": "በስልክ ቁጥር ይመዝገቡ",
|
||||
"enter_phone_number": "የስልክ ቁጥርዎን ያስገቡ። የማረጋገጫ ኮድ እንልክልዎታለን።",
|
||||
"login_with_email": "በኢሜይል ይግቡ",
|
||||
|
|
@ -22,7 +24,7 @@
|
|||
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
||||
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
||||
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
||||
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።" ,
|
||||
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||
"terms_of_services": "የአገልግሎት ውሎች",
|
||||
"and": "እና",
|
||||
"privacy_policy": "የግላዊነት ፖሊሲ",
|
||||
|
|
@ -33,9 +35,9 @@
|
|||
"code_sent_to_email": "ኮዱ ወደ ኢሜል ተልኳል",
|
||||
"resend_code_in": "ኮዱን እንደገና ለመላክ የቀረው ጊዜ",
|
||||
"reset_password": " የይለፍ ቃልን ይቀይሩ",
|
||||
"enter_email_reset_code": "ኢሜይልዎን ያስገቡ። የይለፍ ቃል መለወጫ ኮድ እንልክልዎታለን።" ,
|
||||
"enter_email_reset_code": "ኢሜይልዎን ያስገቡ። የይለፍ ቃል መለወጫ ኮድ እንልክልዎታለን።",
|
||||
"please_wait": "እባክዎ ይጠብቁ",
|
||||
"reset_code_sent": "የመቀየሪያ ኮድ በተሳካ ሁኔታ ተልኳል" ,
|
||||
"reset_code_sent": "የመቀየሪያ ኮድ በተሳካ ሁኔታ ተልኳል",
|
||||
"reset_code": " የመቀየሪያ ኮድ ",
|
||||
"new_password": "አዲስ የይለፍ ቃል",
|
||||
"logged_in_successfully": "በተሳካ ሁኔታ ገብተዋል",
|
||||
|
|
@ -43,14 +45,14 @@
|
|||
"continue_learning": "መማርን ይቀጥሉ",
|
||||
"start_learning": "ትምህርትን ይጀምሩ",
|
||||
"completed": "ተጠናቋል",
|
||||
"take_practice": " ልምምድ ያድርጉ",
|
||||
"take_practice": "ልምምድ ያድርጉ",
|
||||
"your_current_level": "የአሁኑ ደረጃዎ",
|
||||
"overall_progress": "አጠቃላይ እድገት",
|
||||
"great_work": "በርቱ! በጣም ጥሩ እየሰሩ ነው",
|
||||
"view_module": "ሞጁሉን ይመልከቱ",
|
||||
"progress": "እድገት",
|
||||
"keep_going": " ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": " በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"keep_going": "ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": "በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"practice": "ልምምድ",
|
||||
"start": "ጀምር",
|
||||
"in_progress": "በሂደት ላይ",
|
||||
|
|
@ -134,7 +136,7 @@
|
|||
"ask_you_few_actions": "ጥቂት ጥያቄዎችን እጠይቅሃለሁ፣ አንተም በተፈጥሮ መልስ ልትሰጥ ትችላለህ።",
|
||||
"begin_level_practice": "የደረጃ ልምምድን ጀምር",
|
||||
"lets_practice_course": "የኮርሱን ልምምድ እንለማመድ",
|
||||
"lets_quick_practice": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"lets_quick_review": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"speaking": "እየተናገረ ነው",
|
||||
"you_have_finished_practice": "ልምምድህን አጠናቀቅህ",
|
||||
"view_results": "ውጤቶቼን እይ",
|
||||
|
|
@ -153,20 +155,47 @@
|
|||
"what_should_we_call_you": "ምን ብለን እንጠራህ?",
|
||||
"name_for_personalization": "በመማር ጉዞህ ውስጥ ለግል ለማድረግ ስምህን እንጠቀማለን።",
|
||||
"choose_your_gender": "ጾታህን ምረጥ",
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"age_range": "በየትኛው የእድሜ ክልል ውስጥ ነህ?",
|
||||
"age_for_personalization": "በእድሜህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"educational_background": "አሁን ያለህ የትምህርት ደረጃ ምንድን ነው?",
|
||||
"education_for_personalization": "ይህ ትምህርቶችን ከልምድህ ጋር እንዲስማሙ ለማድረግ ይረዳናል።",
|
||||
"your_occupation": "ስራህ ምንድን ነው?",
|
||||
"occupation_for_personalization": "በስራህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"location": "ከየት ነህ?",
|
||||
"select_country_region": "አገርህን እና ክልልህን ከተቆልቋይ ዝርዝሩ ምረጥ",
|
||||
"select_country": "አገር ምረጥ",
|
||||
"learning_goal": "የመማር ዓላማህን ምረጥ",
|
||||
"language_goal": "እንግሊዝኛህን ለማሻሻል ዋና ዓላማህ ምንድን ነው?",
|
||||
"your_goal": "ዓላማህ የመማር ጉዞህን እንዲስማማ ለማድረግ ይረዳናል።",
|
||||
"write_your_goal": "ዓላማህን ጻፍ…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||
"skip": "ዝለል",
|
||||
"finish_level": "ደረጃውን አጠናቅቅ",
|
||||
"likely_speaker": "አንተ ምናልባት ተናጋሪ ነህ",
|
||||
"great_job": "በጣም ጥሩ ስራ! ለመሻሻል ቀጣዩ ደረጃህ ይኸው ነው።",
|
||||
"lets_start_practice": "ልምምድህን እንጀምር",
|
||||
"welcome_abroad": "እንኳን ደህና መጣህ",
|
||||
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
||||
"finish": "አጠናቅቅ",
|
||||
"finish_all_practice_lesson": "ይህን ልምምድ ለመውሰድ የቀድሞውን የትምህርት ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_module": "የሞጁሉን ልምምድ ለመውሰድ የትምህርት ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_course": "የኮርሱን ልምምድ ለመውሰድ የሞጁል ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_module": "ይህን ልምምድ ለመውሰድ የቀድሞውን የሞጁል ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_course": "ይህን ለመውሰድ የቀድሞውን የኮርስ ልምምድ ያጠናቅቁ",
|
||||
"track_journey": "የትምህርት ጉዞዎን ይከታተሉ እና በጊዜ ሂደት ያሳዩትን እድገት ይመልከቱ።",
|
||||
"learn_english": "እንግሊዝኛ ይማሩ",
|
||||
"keep_momentum":"በጣም ጥሩ ስራ! በዚሁ ብርታት ይቀጥሉ።",
|
||||
"completed_practices": "የተጠናቀቁ ልምምዶች",
|
||||
"total_practices": "ጠቅላላ ልምምዶች",
|
||||
"progress_percentage": "የእድገት መቶኛ"
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,159 +1,199 @@
|
|||
|
||||
{"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
"dont_have_account": "Don't have an account? Register",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"forgot_password": "Forgot password?",
|
||||
"cont": "Continue",
|
||||
"register": "Register",
|
||||
"login_with_google": "Login with Google",
|
||||
"or": "Or",
|
||||
"login_with_phone": "Login with phone number",
|
||||
"create_account": "Create an account",
|
||||
"already_have_account": "Already have an account?",
|
||||
"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.",
|
||||
"login_with_email": "Login with email",
|
||||
"create_password": "Create password",
|
||||
"confirm_password": "Confirm password",
|
||||
"eight_character_minimum": "8 characters minimum",
|
||||
"password_math": "password match",
|
||||
"sign_up_agreement": "By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
||||
"terms_of_services": "Terms of Service",
|
||||
"and": "and",
|
||||
"privacy_policy": "Privacy Policy",
|
||||
"register_with_email": "Register with email",
|
||||
"verification_code": "Verification Code",
|
||||
"resend_code": "Resend Code",
|
||||
"code_sent_to_phone": "Code sent to your number",
|
||||
"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.",
|
||||
"please_wait": "Please wait",
|
||||
"reset_code_sent": "Reset code sent successfully",
|
||||
"reset_code": "Reset code",
|
||||
"new_password": "New password",
|
||||
"logged_in_successfully": "Logged in successfully",
|
||||
"continue_learning": "Continue Learning",
|
||||
"start_learning": "Start Learning",
|
||||
"completed": "Completed",
|
||||
"view_course": "View course",
|
||||
"take_practice": "Take practice",
|
||||
"your_current_level": "Your current level",
|
||||
"overall_progress": "Overall progress",
|
||||
"great_work": "Keep up the great work! You're doing amazing",
|
||||
"view_module": "View module",
|
||||
"progress": "Progress",
|
||||
"keep_going": "Let's keep going - you're more than half there",
|
||||
"lessons_in_module": "Lessons in this module",
|
||||
"practice": "Practice",
|
||||
"start": "Start",
|
||||
"in_progress": "In Progress",
|
||||
"hello": "Hello",
|
||||
"ready_to_learn": "Ready to keep learning English today",
|
||||
"learn": "Learn",
|
||||
"course": "Course",
|
||||
"profile": "Profile",
|
||||
"speaking_partner": "Speaking partner",
|
||||
"practice_what_you_learned": "Let's practice what you just learnt",
|
||||
"practice_questions": "I will ask you a few questions and you can respond",
|
||||
"start_practice": "Start practice",
|
||||
"almost_there": "You're almost there",
|
||||
"finish_session": "Finish the session to see your progress",
|
||||
"continue_practice": "Continue practice",
|
||||
"end_session": "End session",
|
||||
"tap_start_to_listen": "Tap the start button to listen",
|
||||
"practice_speaking": "Practice speaking",
|
||||
"tap_microphone": "Tap the microphone to speak",
|
||||
"reply": "Reply",
|
||||
"cancel": "Cancel",
|
||||
"you_are_speaking": "You're speaking",
|
||||
"practice_completed": "Practice completed!",
|
||||
"great_improvement": "You sound more confident this time, great improvement",
|
||||
"practice_again": "Practice again",
|
||||
"conversation_review": "Conversation review",
|
||||
"result": "Result",
|
||||
"quick_tip": "Quick tip",
|
||||
"retry": "Retry",
|
||||
"completed_a1": "Yay, you've completed A1",
|
||||
"analyzing_speaking": "We're now analyzing your speaking skill",
|
||||
"view_profile": "View profile",
|
||||
"hi": "Hi",
|
||||
"edit_profile": "Edit profile",
|
||||
"first_name": "First name",
|
||||
"last_name": "Last name",
|
||||
"gender": "Gender",
|
||||
"male": "Male",
|
||||
"female": "Female",
|
||||
"phone_number": "Phone number",
|
||||
"country": "Country",
|
||||
"region": "Region",
|
||||
"select_region": "Select region",
|
||||
"enter_your_city": "Enter your city",
|
||||
"occupation": "Occupation",
|
||||
"select_occupation": "Select occupation",
|
||||
"save_changes": "Save changes",
|
||||
"my_progress": "My progress",
|
||||
"track_your_achievement": "Track your achievements and learning streak",
|
||||
"account_and_privacy": "Account & Privacy",
|
||||
"manage_settings": "Manage settings and app preference",
|
||||
"support": "Support",
|
||||
"get_help": "Get help through phone or Telegram",
|
||||
"logout": "Logout",
|
||||
"app_settings": "App settings",
|
||||
"legal_and_information": "Legal & Information",
|
||||
"change_language": "Change language",
|
||||
"terms_and_conditions":"Terms & Conditions",
|
||||
"delete_account": "Delete account",
|
||||
"language_preference": "Language preference",
|
||||
"choose_your_language": "Choose your language",
|
||||
"switch_language_anytime": "You can switch languages anytime",
|
||||
"need_help": "Need help?",
|
||||
"call_support": "Call support",
|
||||
"talk_with_support": "Talk with our support team directly",
|
||||
"telegram_support": "Telegram support",
|
||||
"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",
|
||||
"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",
|
||||
"keep_up_the_great_work": "Keep up the great work! You\\'re doing amazing.",
|
||||
"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_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.",
|
||||
"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!",
|
||||
"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!",
|
||||
"you_have_completed": "Yay, you’ve completed",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"want_to_quit": "Are you sure you want to quit?",
|
||||
"required_field": "The field is required",
|
||||
"enter_full_name": "Enter your full name",
|
||||
"invalid_email": "Invalid email format",
|
||||
"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.",
|
||||
"choose_your_gender": "Choose your gender?",
|
||||
"gender_for_personalization": "We’ll personalize your learning experience based on your gender."
|
||||
|
||||
|
||||
}
|
||||
{
|
||||
"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
"dont_have_account": "Don't have an account?",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"forgot_password": "Forgot password?",
|
||||
"cont": "Continue",
|
||||
"register": "Register",
|
||||
"login_with_google": "Login with Google",
|
||||
"login_with_apple": "Login with Apple",
|
||||
"or": "Or",
|
||||
"login_with_phone": "Login with phone number",
|
||||
"create_account": "Create an account",
|
||||
"already_have_account": "Already have an account?",
|
||||
"login": "Login",
|
||||
"register_with_google": "Register with Google",
|
||||
"register_with_apple": "Register with Apple",
|
||||
"register_with_phone": "Register with phone number",
|
||||
"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’",
|
||||
"terms_of_services": "Terms of Service",
|
||||
"and": "and",
|
||||
"privacy_policy": "Privacy Policy",
|
||||
"register_with_email": "Register with email",
|
||||
"verification_code": "Verification Code",
|
||||
"resend_code": "Resend Code",
|
||||
"code_sent_to_phone": "Code sent to your number",
|
||||
"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.",
|
||||
"please_wait": "Please wait",
|
||||
"reset_code_sent": "Reset code sent successfully",
|
||||
"reset_code": "Reset code",
|
||||
"new_password": "New password",
|
||||
"logged_in_successfully": "Logged in successfully",
|
||||
"continue_learning": "Continue Learning",
|
||||
"start_learning": "Start Learning",
|
||||
"completed": "Completed",
|
||||
"view_course": "View course",
|
||||
"take_practice": "Take practice",
|
||||
"your_current_level": "Your current level",
|
||||
"overall_progress": "Overall progress",
|
||||
"great_work": "Keep up the great work! You're doing amazing",
|
||||
"view_module": "View module",
|
||||
"progress": "Progress",
|
||||
"keep_going": "Let's keep going - you're more than half there",
|
||||
"lessons_in_module": "Lessons in this module",
|
||||
"practice": "Practice",
|
||||
"start": "Start",
|
||||
"in_progress": "In Progress",
|
||||
"hello": "Hello",
|
||||
"ready_to_learn": "Ready to keep learning English today",
|
||||
"learn": "Learn",
|
||||
"course": "Course",
|
||||
"profile": "Profile",
|
||||
"speaking_partner": "Speaking partner",
|
||||
"practice_what_you_learned": "Let's practice what you just learnt",
|
||||
"practice_questions": "I will ask you a few questions and you can respond",
|
||||
"start_practice": "Start practice",
|
||||
"almost_there": "You're almost there",
|
||||
"finish_session": "Finish the session to see your progress",
|
||||
"continue_practice": "Continue practice",
|
||||
"end_session": "End session",
|
||||
"tap_start_to_listen": "Tap the start button to listen",
|
||||
"practice_speaking": "Practice speaking",
|
||||
"tap_microphone": "Tap the microphone to speak",
|
||||
"reply": "Reply",
|
||||
"cancel": "Cancel",
|
||||
"you_are_speaking": "You're speaking",
|
||||
"practice_completed": "Practice completed!",
|
||||
"great_improvement": "You sound more confident this time, great improvement",
|
||||
"practice_again": "Practice again",
|
||||
"conversation_review": "Conversation review",
|
||||
"result": "Result",
|
||||
"quick_tip": "Quick tip",
|
||||
"retry": "Retry",
|
||||
"completed_a1": "Yay, you've completed A1",
|
||||
"analyzing_speaking": "We're now analyzing your speaking skill",
|
||||
"view_profile": "View profile",
|
||||
"hi": "Hi",
|
||||
"edit_profile": "Edit profile",
|
||||
"first_name": "First name",
|
||||
"last_name": "Last name",
|
||||
"gender": "Gender",
|
||||
"male": "Male",
|
||||
"female": "Female",
|
||||
"phone_number": "Phone number",
|
||||
"country": "Country",
|
||||
"region": "Region",
|
||||
"select_region": "Select region",
|
||||
"enter_your_city": "Enter your city",
|
||||
"occupation": "Occupation",
|
||||
"select_occupation": "Select occupation",
|
||||
"save_changes": "Save changes",
|
||||
"my_progress": "My progress",
|
||||
"track_your_achievement": "Track your achievements and learning streak",
|
||||
"account_and_privacy": "Account & Privacy",
|
||||
"manage_settings": "Manage settings and app preference",
|
||||
"support": "Support",
|
||||
"get_help": "Get help through phone or Telegram",
|
||||
"logout": "Logout",
|
||||
"app_settings": "App settings",
|
||||
"legal_and_information": "Legal & Information",
|
||||
"change_language": "Change language",
|
||||
"terms_and_conditions": "Terms & Conditions",
|
||||
"delete_account": "Delete account",
|
||||
"language_preference": "Language preference",
|
||||
"choose_your_language": "Choose your language",
|
||||
"switch_language_anytime": "You can switch languages anytime",
|
||||
"need_help": "Need help?",
|
||||
"call_support": "Call support",
|
||||
"talk_with_support": "Talk with our support team directly",
|
||||
"telegram_support": "Telegram support",
|
||||
"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",
|
||||
"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",
|
||||
"keep_up_the_great_work": "Keep up the great work! You're doing amazing.",
|
||||
"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_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.",
|
||||
"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!",
|
||||
"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!",
|
||||
"you_have_completed": "Yay, you’ve completed",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"want_to_quit": "Are you sure you want to quit?",
|
||||
"required_field": "The field is required",
|
||||
"enter_full_name": "Enter your full name",
|
||||
"invalid_email": "Invalid email format",
|
||||
"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.",
|
||||
"choose_your_gender": "Choose 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.",
|
||||
"educational_background": "What’s your current educational level?",
|
||||
"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.",
|
||||
"location": "Where are you from?",
|
||||
"select_country_region": "Select your country and region from the dropdown",
|
||||
"select_country": "Select country",
|
||||
"learning_goal": "Choose your learning goal.",
|
||||
"language_goal": "What’s your main goal for improving your English?",
|
||||
"your_goal": "Your goal helps us tailor your learning journey.",
|
||||
"write_your_goal": "Write your goal…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"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.",
|
||||
"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.",
|
||||
"skip": "Skip",
|
||||
"finish_level": "Finish Level",
|
||||
"likely_speaker": "You’re likely speaker of",
|
||||
"great_job": "Great Job! Here’s your next step to keep improving.",
|
||||
"lets_start_practice": "Let's start your practice",
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,5 @@
|
|||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>13.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
|||
46
ios/Podfile
Normal file
46
ios/Podfile
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '15.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def flutter_root
|
||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||
unless File.exist?(generated_xcode_build_settings_path)
|
||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||
end
|
||||
|
||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||
return matches[1].strip if matches
|
||||
end
|
||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||
end
|
||||
|
||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||
|
||||
flutter_ios_podfile_setup
|
||||
|
||||
target 'Runner' do
|
||||
use_frameworks!
|
||||
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
target 'RunnerTests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
|
||||
end
|
||||
end
|
||||
end
|
||||
28
ios/Podfile.lock
Normal file
28
ios/Podfile.lock
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
PODS:
|
||||
- Flutter (1.0.0)
|
||||
- flutter_phone_direct_caller (0.0.1):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_phone_direct_caller (from `.symlinks/plugins/flutter_phone_direct_caller/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_phone_direct_caller:
|
||||
:path: ".symlinks/plugins/flutter_phone_direct_caller/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_phone_direct_caller: 7d5d72794577b96f12b4b6da13a9ef90ba438665
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
|
||||
PODFILE CHECKSUM: 4b015915ec662986b54bf30ab778da63f7dda016
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
|
@ -11,9 +11,13 @@
|
|||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
|
||||
896DC9E666DF8098D827C010 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9793345F00B89E38C23EBB8 /* Pods_RunnerTests.framework */; };
|
||||
8E1B3E4A2C540A0B00F51C11 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8E1B3E492C540A0B00F51C11 /* GoogleService-Info.plist */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
99CE3BFD23F69C6D49568DE0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93B517F10FA92BB14B3CDC5A /* Pods_Runner.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -42,12 +46,19 @@
|
|||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
18336A33563E3B5B5B9974CC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
20E44080F42EAC6B045A6D89 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
69301B8842E33A5CD16999A8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
74C1B93B8C9E9562FD058B56 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
8E1B3E492C540A0B00F51C11 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
93B517F10FA92BB14B3CDC5A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
|
@ -55,13 +66,27 @@
|
|||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
ACEEA7C32CFC6E9900D60211 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
A8C2A6C7D1D99F7BA12EAF94 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
F1F6AAAC52D909E27AEDEFC0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
F9793345F00B89E38C23EBB8 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
932D3841F05A890DB5B188A4 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
896DC9E666DF8098D827C010 /* Pods_RunnerTests.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
|
||||
99CE3BFD23F69C6D49568DE0 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -76,9 +101,32 @@
|
|||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
633D6DCBF46C9B68DCD511FE /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
93B517F10FA92BB14B3CDC5A /* Pods_Runner.framework */,
|
||||
F9793345F00B89E38C23EBB8 /* Pods_RunnerTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8096B886696A019BCD318B6B /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
74C1B93B8C9E9562FD058B56 /* Pods-Runner.debug.xcconfig */,
|
||||
69301B8842E33A5CD16999A8 /* Pods-Runner.release.xcconfig */,
|
||||
F1F6AAAC52D909E27AEDEFC0 /* Pods-Runner.profile.xcconfig */,
|
||||
20E44080F42EAC6B045A6D89 /* Pods-RunnerTests.debug.xcconfig */,
|
||||
18336A33563E3B5B5B9974CC /* Pods-RunnerTests.release.xcconfig */,
|
||||
A8C2A6C7D1D99F7BA12EAF94 /* Pods-RunnerTests.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
|
|
@ -94,6 +142,8 @@
|
|||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||
8096B886696A019BCD318B6B /* Pods */,
|
||||
633D6DCBF46C9B68DCD511FE /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
|
@ -113,6 +163,8 @@
|
|||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
8E1B3E492C540A0B00F51C11 /* GoogleService-Info.plist */,
|
||||
ACEEA7C32CFC6E9900D60211 /* Runner.entitlements */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
|
|
@ -128,8 +180,10 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
94F71B9A2AE7A340918A2B71 /* [CP] Check Pods Manifest.lock */,
|
||||
331C807D294A63A400263BE5 /* Sources */,
|
||||
331C807F294A63A400263BE5 /* Resources */,
|
||||
932D3841F05A890DB5B188A4 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -145,18 +199,24 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
EDCDA15227D1D12483183F4E /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
DE13979F911D29D4A95BCE2F /* [CP] Embed Pods Frameworks */,
|
||||
E684A37538F596FB5432DE3F /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Runner;
|
||||
packageProductDependencies = (
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
|
||||
);
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
|
|
@ -178,6 +238,11 @@
|
|||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1100;
|
||||
SystemCapabilities = {
|
||||
com.apple.SignInWithApple = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -190,6 +255,9 @@
|
|||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
packageReferences = (
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
|
||||
);
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
|
|
@ -215,6 +283,7 @@
|
|||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
8E1B3E4A2C540A0B00F51C11 /* GoogleService-Info.plist in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -238,6 +307,28 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
94F71B9A2AE7A340918A2B71 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
|
@ -253,6 +344,62 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
DE13979F911D29D4A95BCE2F /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E684A37538F596FB5432DE3F /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
EDCDA15227D1D12483183F4E /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
|
@ -346,7 +493,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
|
@ -361,15 +508,23 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K73ZX6D43Q;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.app;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.testapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Yimaru LMS TestApp App Store Profile";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
|
|
@ -378,13 +533,14 @@
|
|||
};
|
||||
331C8088294A63A400263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 20E44080F42EAC6B045A6D89 /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.app.RunnerTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.testapp.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
|
|
@ -395,13 +551,14 @@
|
|||
};
|
||||
331C8089294A63A400263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 18336A33563E3B5B5B9974CC /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.app.RunnerTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.testapp.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||
|
|
@ -410,13 +567,14 @@
|
|||
};
|
||||
331C808A294A63A400263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = A8C2A6C7D1D99F7BA12EAF94 /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.app.RunnerTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.testapp.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||
|
|
@ -472,7 +630,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
|
|
@ -523,7 +681,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
|
@ -540,15 +698,23 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K73ZX6D43Q;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.app;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.testapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Yimaru LMS TestApp App Store Profile";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -562,15 +728,23 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K73ZX6D43Q;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.app;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yimaru.lms.testapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Yimaru LMS TestApp App Store Profile";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
|
|
@ -611,6 +785,20 @@
|
|||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCLocalSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "abseil-cpp-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/abseil-cpp-binary.git",
|
||||
"state" : {
|
||||
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
|
||||
"version" : "1.2024072200.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "app-check",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/app-check.git",
|
||||
"state" : {
|
||||
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
|
||||
"version" : "11.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "appauth-ios",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/openid/AppAuth-iOS.git",
|
||||
"state" : {
|
||||
"revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e",
|
||||
"version" : "2.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "firebase-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "8d5b4189f1f482df8d5c58c9985ea70491ef5382",
|
||||
"version" : "12.14.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "flutterfire",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/flutterfire",
|
||||
"state" : {
|
||||
"revision" : "05731e3fb091093546db363e379bff166f7286a3",
|
||||
"version" : "4.4.0-firebase-core-swift"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "google-ads-on-device-conversion-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "9bfcc6cf435b2e7c5562c1900b8680c594fa9a64",
|
||||
"version" : "3.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleappmeasurement",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git",
|
||||
"state" : {
|
||||
"revision" : "219e564a8510e983e675c94f77f7f7c50049f22d",
|
||||
"version" : "12.14.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googledatatransport",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git",
|
||||
"state" : {
|
||||
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
|
||||
"version" : "10.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googlesignin-ios",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleSignIn-iOS.git",
|
||||
"state" : {
|
||||
"revision" : "913b4005ea26aebe1c97d54e35ad82a515924c71",
|
||||
"version" : "9.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleutilities",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleUtilities.git",
|
||||
"state" : {
|
||||
"revision" : "60da361632d0de02786f709bdc0c4df340f7613e",
|
||||
"version" : "8.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/grpc-binary.git",
|
||||
"state" : {
|
||||
"revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6",
|
||||
"version" : "1.69.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gtm-session-fetcher",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/gtm-session-fetcher.git",
|
||||
"state" : {
|
||||
"revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b",
|
||||
"version" : "3.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gtmappauth",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GTMAppAuth.git",
|
||||
"state" : {
|
||||
"revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16",
|
||||
"version" : "5.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "interop-ios-for-google-sdks",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
|
||||
"state" : {
|
||||
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
|
||||
"version" : "101.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "leveldb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/leveldb.git",
|
||||
"state" : {
|
||||
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
|
||||
"version" : "1.22.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nanopb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/nanopb.git",
|
||||
"state" : {
|
||||
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
|
||||
"version" : "2.30910.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "promises",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/promises.git",
|
||||
"state" : {
|
||||
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
|
||||
"version" : "2.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"state" : {
|
||||
"revision" : "fea17c02d767f46b23070fdfdacc28a03a39232a",
|
||||
"version" : "1.5.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
|
|
@ -5,6 +5,24 @@
|
|||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Prepare Flutter Framework Script"
|
||||
scriptText = "/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
|
|
|
|||
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
|
|
@ -4,4 +4,7 @@
|
|||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
|
|||
167
ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
Normal file
167
ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "abseil-cpp-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/abseil-cpp-binary.git",
|
||||
"state" : {
|
||||
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
|
||||
"version" : "1.2024072200.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "app-check",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/app-check.git",
|
||||
"state" : {
|
||||
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
|
||||
"version" : "11.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "appauth-ios",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/openid/AppAuth-iOS.git",
|
||||
"state" : {
|
||||
"revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e",
|
||||
"version" : "2.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "firebase-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "8d5b4189f1f482df8d5c58c9985ea70491ef5382",
|
||||
"version" : "12.14.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "flutterfire",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/flutterfire",
|
||||
"state" : {
|
||||
"revision" : "05731e3fb091093546db363e379bff166f7286a3",
|
||||
"version" : "4.4.0-firebase-core-swift"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "google-ads-on-device-conversion-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "9bfcc6cf435b2e7c5562c1900b8680c594fa9a64",
|
||||
"version" : "3.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleappmeasurement",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git",
|
||||
"state" : {
|
||||
"revision" : "219e564a8510e983e675c94f77f7f7c50049f22d",
|
||||
"version" : "12.14.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googledatatransport",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git",
|
||||
"state" : {
|
||||
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
|
||||
"version" : "10.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googlesignin-ios",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleSignIn-iOS.git",
|
||||
"state" : {
|
||||
"revision" : "913b4005ea26aebe1c97d54e35ad82a515924c71",
|
||||
"version" : "9.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleutilities",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleUtilities.git",
|
||||
"state" : {
|
||||
"revision" : "60da361632d0de02786f709bdc0c4df340f7613e",
|
||||
"version" : "8.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/grpc-binary.git",
|
||||
"state" : {
|
||||
"revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6",
|
||||
"version" : "1.69.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gtm-session-fetcher",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/gtm-session-fetcher.git",
|
||||
"state" : {
|
||||
"revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b",
|
||||
"version" : "3.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gtmappauth",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GTMAppAuth.git",
|
||||
"state" : {
|
||||
"revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16",
|
||||
"version" : "5.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "interop-ios-for-google-sdks",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
|
||||
"state" : {
|
||||
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
|
||||
"version" : "101.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "leveldb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/leveldb.git",
|
||||
"state" : {
|
||||
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
|
||||
"version" : "1.22.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nanopb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/nanopb.git",
|
||||
"state" : {
|
||||
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
|
||||
"version" : "2.30910.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "promises",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/promises.git",
|
||||
"state" : {
|
||||
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
|
||||
"version" : "2.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"state" : {
|
||||
"revision" : "fea17c02d767f46b23070fdfdacc28a03a39232a",
|
||||
"version" : "1.5.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
|
|
@ -2,12 +2,15 @@ import Flutter
|
|||
import UIKit
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
|
||||
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
ios/Runner/GoogleService-Info.plist
Normal file
36
ios/Runner/GoogleService-Info.plist
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CLIENT_ID</key>
|
||||
<string>900714037062-3qqf7urii8vg99id91nmmuvom3fm5c1u.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.900714037062-3qqf7urii8vg99id91nmmuvom3fm5c1u</string>
|
||||
<key>ANDROID_CLIENT_ID</key>
|
||||
<string>900714037062-4trqu7ln6en4kcm6gadk0uo01qijn1mk.apps.googleusercontent.com</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyDbaGD47oUJOyn9n3b0pbH6ozmbGyIlOKk</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>900714037062</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.yimaru.lms.testapp</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>yimaru-academy-5e7e2</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>yimaru-academy-5e7e2.firebasestorage.app</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:900714037062:ios:45b484d79222c3ab4e6f47</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,51 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Yimaru App</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>yimaru_app</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.900714037062-3qqf7urii8vg99id91nmmuvom3fm5c1u</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Yimaru App</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>yimaru_app</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneClassName</key>
|
||||
<string>UIWindowScene</string>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>flutter</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>FlutterSceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
10
ios/Runner/Runner.entitlements
Normal file
10
ios/Runner/Runner.entitlements
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -44,19 +44,21 @@ import 'package:yimaru_app/services/audio_player_service.dart';
|
|||
import 'package:yimaru_app/services/voice_recorder_service.dart';
|
||||
import 'package:yimaru_app/services/in_app_update_service.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
||||
import 'package:yimaru_app/services/vimeo_service.dart';
|
||||
import 'package:yimaru_app/services/url_launcher_service.dart';
|
||||
import 'package:yimaru_app/services/phone_caller_service.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_subscription/learn_subscription_view.dart';
|
||||
import 'package:yimaru_app/ui/views/arif_pay/arif_pay_view.dart';
|
||||
import 'package:yimaru_app/services/learn_service.dart';
|
||||
import 'package:yimaru_app/ui/views/course_catalog/course_catalog_view.dart';
|
||||
import 'package:yimaru_app/ui/views/course_unit/course_unit_view.dart';
|
||||
import 'package:yimaru_app/services/localization_service.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/landing_view.dart';
|
||||
import 'package:yimaru_app/ui/views/course_module/course_module_view.dart';
|
||||
import 'package:yimaru_app/services/onboarding_service.dart';
|
||||
import 'package:yimaru_app/services/apple_auth_service.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
||||
import 'package:yimaru_app/ui/views/payment/payment_view.dart';
|
||||
// @stacked-import
|
||||
|
||||
@StackedApp(
|
||||
|
|
@ -88,15 +90,14 @@ import 'package:yimaru_app/ui/views/course_module/course_module_view.dart';
|
|||
MaterialRoute(page: DuolingoView),
|
||||
MaterialRoute(page: CourseView),
|
||||
MaterialRoute(page: LearnProgramView),
|
||||
MaterialRoute(page: LearnCourseView),
|
||||
MaterialRoute(page: AssessmentView),
|
||||
MaterialRoute(page: LearnSubscriptionView),
|
||||
MaterialRoute(page: ArifPayView),
|
||||
MaterialRoute(page: CourseCatalogView),
|
||||
MaterialRoute(page: CourseUnitView),
|
||||
MaterialRoute(page: LandingView),
|
||||
MaterialRoute(page: CourseModuleView),
|
||||
MaterialRoute(page: LearnCourseView),
|
||||
MaterialRoute(page: PaymentView),
|
||||
// @stacked-route
|
||||
],
|
||||
dependencies: [
|
||||
|
|
@ -123,6 +124,8 @@ import 'package:yimaru_app/ui/views/course_module/course_module_view.dart';
|
|||
LazySingleton(classType: PhoneCallerService),
|
||||
LazySingleton(classType: LearnService),
|
||||
LazySingleton(classType: LocalizationService),
|
||||
LazySingleton(classType: OnboardingService),
|
||||
LazySingleton(classType: AppleAuthService),
|
||||
// @stacked-service
|
||||
],
|
||||
bottomsheets: [
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import 'package:stacked_services/src/navigation/navigation_service.dart';
|
|||
import 'package:stacked_shared/stacked_shared.dart';
|
||||
|
||||
import '../services/api_service.dart';
|
||||
import '../services/apple_auth_service.dart';
|
||||
import '../services/audio_player_service.dart';
|
||||
import '../services/authentication_service.dart';
|
||||
import '../services/course_service.dart';
|
||||
|
|
@ -24,6 +25,7 @@ import '../services/in_app_update_service.dart';
|
|||
import '../services/learn_service.dart';
|
||||
import '../services/localization_service.dart';
|
||||
import '../services/notification_service.dart';
|
||||
import '../services/onboarding_service.dart';
|
||||
import '../services/permission_handler_service.dart';
|
||||
import '../services/phone_caller_service.dart';
|
||||
import '../services/secure_storage_service.dart';
|
||||
|
|
@ -65,4 +67,6 @@ Future<void> setupLocator(
|
|||
locator.registerLazySingleton(() => PhoneCallerService());
|
||||
locator.registerLazySingleton(() => LearnService());
|
||||
locator.registerLazySingleton(() => LocalizationService());
|
||||
locator.registerLazySingleton(() => OnboardingService());
|
||||
locator.registerLazySingleton(() => AppleAuthService());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:flutter/material.dart' as _i37;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' as _i37;
|
||||
import 'package:stacked/stacked.dart' as _i1;
|
||||
import 'package:stacked_services/stacked_services.dart' as _i46;
|
||||
import 'package:stacked_services/stacked_services.dart' as _i47;
|
||||
import 'package:yimaru_app/models/course.dart' as _i42;
|
||||
import 'package:yimaru_app/models/course_catalog.dart' as _i44;
|
||||
import 'package:yimaru_app/models/course_lesson.dart' as _i43;
|
||||
|
|
@ -17,33 +17,33 @@ import 'package:yimaru_app/models/course_module.dart' as _i45;
|
|||
import 'package:yimaru_app/models/learn_course.dart' as _i38;
|
||||
import 'package:yimaru_app/models/learn_lesson.dart' as _i40;
|
||||
import 'package:yimaru_app/models/learn_module.dart' as _i39;
|
||||
import 'package:yimaru_app/models/learn_subscription.dart' as _i46;
|
||||
import 'package:yimaru_app/ui/common/enmus.dart' as _i41;
|
||||
import 'package:yimaru_app/ui/views/account_privacy/account_privacy_view.dart'
|
||||
as _i9;
|
||||
import 'package:yimaru_app/ui/views/arif_pay/arif_pay_view.dart' as _i32;
|
||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i30;
|
||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i29;
|
||||
import 'package:yimaru_app/ui/views/call_support/call_support_view.dart'
|
||||
as _i12;
|
||||
import 'package:yimaru_app/ui/views/course/course_view.dart' as _i27;
|
||||
import 'package:yimaru_app/ui/views/course_catalog/course_catalog_view.dart'
|
||||
as _i33;
|
||||
as _i31;
|
||||
import 'package:yimaru_app/ui/views/course_lesson_detail/course_lesson_detail_view.dart'
|
||||
as _i25;
|
||||
import 'package:yimaru_app/ui/views/course_module/course_module_view.dart'
|
||||
as _i36;
|
||||
as _i34;
|
||||
import 'package:yimaru_app/ui/views/course_payment/course_payment_view.dart'
|
||||
as _i23;
|
||||
import 'package:yimaru_app/ui/views/course_unit/course_unit_view.dart' as _i34;
|
||||
import 'package:yimaru_app/ui/views/course_unit/course_unit_view.dart' as _i32;
|
||||
import 'package:yimaru_app/ui/views/downloads/downloads_view.dart' as _i7;
|
||||
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i26;
|
||||
import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i24;
|
||||
import 'package:yimaru_app/ui/views/forget_password/forget_password_view.dart'
|
||||
as _i20;
|
||||
import 'package:yimaru_app/ui/views/home/home_view.dart' as _i2;
|
||||
import 'package:yimaru_app/ui/views/landing/landing_view.dart' as _i35;
|
||||
import 'package:yimaru_app/ui/views/landing/landing_view.dart' as _i33;
|
||||
import 'package:yimaru_app/ui/views/language/language_view.dart' as _i13;
|
||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart'
|
||||
as _i29;
|
||||
as _i35;
|
||||
import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart'
|
||||
as _i19;
|
||||
import 'package:yimaru_app/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart'
|
||||
|
|
@ -55,9 +55,10 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_view.dart'
|
|||
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'
|
||||
as _i28;
|
||||
import 'package:yimaru_app/ui/views/learn_subscription/learn_subscription_view.dart'
|
||||
as _i31;
|
||||
as _i30;
|
||||
import 'package:yimaru_app/ui/views/login/login_view.dart' as _i17;
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i3;
|
||||
import 'package:yimaru_app/ui/views/payment/payment_view.dart' as _i36;
|
||||
import 'package:yimaru_app/ui/views/privacy_policy/privacy_policy_view.dart'
|
||||
as _i14;
|
||||
import 'package:yimaru_app/ui/views/profile/profile_view.dart' as _i5;
|
||||
|
|
@ -127,14 +128,10 @@ class Routes {
|
|||
|
||||
static const learnProgramView = '/learn-program-view';
|
||||
|
||||
static const learnCourseView = '/learn-course-view';
|
||||
|
||||
static const assessmentView = '/assessment-view';
|
||||
|
||||
static const learnSubscriptionView = '/learn-subscription-view';
|
||||
|
||||
static const arifPayView = '/arif-pay-view';
|
||||
|
||||
static const courseCatalogView = '/course-catalog-view';
|
||||
|
||||
static const courseUnitView = '/course-unit-view';
|
||||
|
|
@ -143,6 +140,9 @@ class Routes {
|
|||
|
||||
static const courseModuleView = '/course-module-view';
|
||||
|
||||
static const learnCourseView = '/learn-course-view';
|
||||
|
||||
static const paymentView = '/payment-view';
|
||||
|
||||
static const all = <String>{
|
||||
homeView,
|
||||
|
|
@ -172,14 +172,14 @@ class Routes {
|
|||
duolingoView,
|
||||
courseView,
|
||||
learnProgramView,
|
||||
learnCourseView,
|
||||
assessmentView,
|
||||
learnSubscriptionView,
|
||||
arifPayView,
|
||||
courseCatalogView,
|
||||
courseUnitView,
|
||||
landingView,
|
||||
courseModuleView,
|
||||
learnCourseView,
|
||||
paymentView,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -293,41 +293,37 @@ class StackedRouter extends _i1.RouterBase {
|
|||
Routes.learnProgramView,
|
||||
page: _i28.LearnProgramView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.learnCourseView,
|
||||
page: _i29.LearnCourseView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.assessmentView,
|
||||
page: _i30.AssessmentView,
|
||||
page: _i29.AssessmentView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.learnSubscriptionView,
|
||||
page: _i31.LearnSubscriptionView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.arifPayView,
|
||||
page: _i32.ArifPayView,
|
||||
page: _i30.LearnSubscriptionView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.courseCatalogView,
|
||||
page: _i33.CourseCatalogView,
|
||||
page: _i31.CourseCatalogView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.courseUnitView,
|
||||
page: _i34.CourseUnitView,
|
||||
page: _i32.CourseUnitView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.landingView,
|
||||
page: _i35.LandingView,
|
||||
page: _i33.LandingView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.courseModuleView,
|
||||
page: _i36.CourseModuleView,
|
||||
page: _i34.CourseModuleView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.learnCourseView,
|
||||
page: _i29.LearnCourseView,
|
||||
page: _i35.LearnCourseView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.paymentView,
|
||||
page: _i36.PaymentView,
|
||||
),
|
||||
];
|
||||
|
||||
|
|
@ -506,6 +502,7 @@ class StackedRouter extends _i1.RouterBase {
|
|||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i21.LearnLessonDetailView(
|
||||
key: args.key,
|
||||
index: args.index,
|
||||
lesson: args.lesson,
|
||||
module: args.module,
|
||||
hasPractice: args.hasPractice),
|
||||
|
|
@ -577,72 +574,72 @@ class StackedRouter extends _i1.RouterBase {
|
|||
settings: data,
|
||||
);
|
||||
},
|
||||
_i29.LearnCourseView: (data) {
|
||||
final args = data.getArgs<LearnCourseViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i29.LearnCourseView(key: args.key, id: args.id),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i30.AssessmentView: (data) {
|
||||
_i29.AssessmentView: (data) {
|
||||
final args = data.getArgs<AssessmentViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) =>
|
||||
_i30.AssessmentView(key: args.key, data: args.data),
|
||||
_i29.AssessmentView(key: args.key, data: args.data),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i31.LearnSubscriptionView: (data) {
|
||||
_i30.LearnSubscriptionView: (data) {
|
||||
final args = data.getArgs<LearnSubscriptionViewArguments>(
|
||||
orElse: () => const LearnSubscriptionViewArguments(),
|
||||
);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i31.LearnSubscriptionView(key: args.key),
|
||||
builder: (context) => _i30.LearnSubscriptionView(key: args.key),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i32.ArifPayView: (data) {
|
||||
final args = data.getArgs<ArifPayViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) =>
|
||||
_i32.ArifPayView(key: args.key, phone: args.phone),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i33.CourseCatalogView: (data) {
|
||||
_i31.CourseCatalogView: (data) {
|
||||
final args = data.getArgs<CourseCatalogViewArguments>(
|
||||
orElse: () => const CourseCatalogViewArguments(),
|
||||
);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i33.CourseCatalogView(key: args.key),
|
||||
builder: (context) => _i31.CourseCatalogView(key: args.key),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i34.CourseUnitView: (data) {
|
||||
_i32.CourseUnitView: (data) {
|
||||
final args = data.getArgs<CourseUnitViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) =>
|
||||
_i34.CourseUnitView(key: args.key, catalog: args.catalog),
|
||||
_i32.CourseUnitView(key: args.key, catalog: args.catalog),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i35.LandingView: (data) {
|
||||
_i33.LandingView: (data) {
|
||||
final args = data.getArgs<LandingViewArguments>(
|
||||
orElse: () => const LandingViewArguments(),
|
||||
);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i35.LandingView(key: args.key),
|
||||
builder: (context) => _i33.LandingView(key: args.key),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i36.CourseModuleView: (data) {
|
||||
_i34.CourseModuleView: (data) {
|
||||
final args = data.getArgs<CourseModuleViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i36.CourseModuleView(
|
||||
builder: (context) => _i34.CourseModuleView(
|
||||
key: args.key, module: args.module, catalog: args.catalog),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i35.LearnCourseView: (data) {
|
||||
final args = data.getArgs<LearnCourseViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i35.LearnCourseView(key: args.key, id: args.id),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i36.PaymentView: (data) {
|
||||
final args = data.getArgs<PaymentViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i36.PaymentView(
|
||||
key: args.key, phone: args.phone, subscription: args.subscription),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@override
|
||||
|
|
@ -1088,6 +1085,7 @@ class ForgetPasswordViewArguments {
|
|||
class LearnLessonDetailViewArguments {
|
||||
const LearnLessonDetailViewArguments({
|
||||
this.key,
|
||||
required this.index,
|
||||
required this.lesson,
|
||||
required this.module,
|
||||
required this.hasPractice,
|
||||
|
|
@ -1095,6 +1093,8 @@ class LearnLessonDetailViewArguments {
|
|||
|
||||
final _i37.Key? key;
|
||||
|
||||
final int index;
|
||||
|
||||
final _i40.LearnLesson lesson;
|
||||
|
||||
final _i39.LearnModule module;
|
||||
|
|
@ -1103,13 +1103,14 @@ class LearnLessonDetailViewArguments {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return '{"key": "$key", "lesson": "$lesson", "module": "$module", "hasPractice": "$hasPractice"}';
|
||||
return '{"key": "$key", "index": "$index", "lesson": "$lesson", "module": "$module", "hasPractice": "$hasPractice"}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant LearnLessonDetailViewArguments other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.key == key &&
|
||||
other.index == index &&
|
||||
other.lesson == lesson &&
|
||||
other.module == module &&
|
||||
other.hasPractice == hasPractice;
|
||||
|
|
@ -1118,6 +1119,7 @@ class LearnLessonDetailViewArguments {
|
|||
@override
|
||||
int get hashCode {
|
||||
return key.hashCode ^
|
||||
index.hashCode ^
|
||||
lesson.hashCode ^
|
||||
module.hashCode ^
|
||||
hasPractice.hashCode;
|
||||
|
|
@ -1328,33 +1330,6 @@ class LearnProgramViewArguments {
|
|||
}
|
||||
}
|
||||
|
||||
class LearnCourseViewArguments {
|
||||
const LearnCourseViewArguments({
|
||||
this.key,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final _i37.Key? key;
|
||||
|
||||
final int id;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{"key": "$key", "id": "$id"}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant LearnCourseViewArguments other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.key == key && other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return key.hashCode ^ id.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
class AssessmentViewArguments {
|
||||
const AssessmentViewArguments({
|
||||
this.key,
|
||||
|
|
@ -1404,33 +1379,6 @@ class LearnSubscriptionViewArguments {
|
|||
}
|
||||
}
|
||||
|
||||
class ArifPayViewArguments {
|
||||
const ArifPayViewArguments({
|
||||
this.key,
|
||||
required this.phone,
|
||||
});
|
||||
|
||||
final _i37.Key? key;
|
||||
|
||||
final String phone;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{"key": "$key", "phone": "$phone"}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ArifPayViewArguments other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.key == key && other.phone == phone;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return key.hashCode ^ phone.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
class CourseCatalogViewArguments {
|
||||
const CourseCatalogViewArguments({this.key});
|
||||
|
||||
|
|
@ -1534,7 +1482,66 @@ class CourseModuleViewArguments {
|
|||
}
|
||||
}
|
||||
|
||||
extension NavigatorStateExtension on _i46.NavigationService {
|
||||
class LearnCourseViewArguments {
|
||||
const LearnCourseViewArguments({
|
||||
this.key,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final _i37.Key? key;
|
||||
|
||||
final int id;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{"key": "$key", "id": "$id"}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant LearnCourseViewArguments other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.key == key && other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return key.hashCode ^ id.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
class PaymentViewArguments {
|
||||
const PaymentViewArguments({
|
||||
this.key,
|
||||
required this.phone,
|
||||
required this.subscription,
|
||||
});
|
||||
|
||||
final _i37.Key? key;
|
||||
|
||||
final String phone;
|
||||
|
||||
final _i46.LearnSubscription subscription;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{"key": "$key", "phone": "$phone", "subscription": "$subscription"}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant PaymentViewArguments other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.key == key &&
|
||||
other.phone == phone &&
|
||||
other.subscription == subscription;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return key.hashCode ^ phone.hashCode ^ subscription.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
extension NavigatorStateExtension on _i47.NavigationService {
|
||||
Future<dynamic> navigateToHomeView({
|
||||
_i37.Key? key,
|
||||
int? routerId,
|
||||
|
|
@ -1844,6 +1851,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
|
||||
Future<dynamic> navigateToLearnLessonDetailView({
|
||||
_i37.Key? key,
|
||||
required int index,
|
||||
required _i40.LearnLesson lesson,
|
||||
required _i39.LearnModule module,
|
||||
required bool hasPractice,
|
||||
|
|
@ -1855,7 +1863,11 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
}) async {
|
||||
return navigateTo<dynamic>(Routes.learnLessonDetailView,
|
||||
arguments: LearnLessonDetailViewArguments(
|
||||
key: key, lesson: lesson, module: module, hasPractice: hasPractice),
|
||||
key: key,
|
||||
index: index,
|
||||
lesson: lesson,
|
||||
module: module,
|
||||
hasPractice: hasPractice),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
|
|
@ -1991,23 +2003,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return navigateTo<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToAssessmentView({
|
||||
_i37.Key? key,
|
||||
required Map<String, dynamic> data,
|
||||
|
|
@ -2041,23 +2036,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToArifPayView({
|
||||
_i37.Key? key,
|
||||
required String phone,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return navigateTo<dynamic>(Routes.arifPayView,
|
||||
arguments: ArifPayViewArguments(key: key, phone: phone),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToCourseCatalogView({
|
||||
_i37.Key? key,
|
||||
int? routerId,
|
||||
|
|
@ -2126,7 +2104,41 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return navigateTo<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToPaymentView({
|
||||
_i37.Key? key,
|
||||
required String phone,
|
||||
required _i46.LearnSubscription subscription,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return navigateTo<dynamic>(Routes.paymentView,
|
||||
arguments: PaymentViewArguments(
|
||||
key: key, phone: phone, subscription: subscription),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithHomeView({
|
||||
_i37.Key? key,
|
||||
|
|
@ -2437,6 +2449,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
|
||||
Future<dynamic> replaceWithLearnLessonDetailView({
|
||||
_i37.Key? key,
|
||||
required int index,
|
||||
required _i40.LearnLesson lesson,
|
||||
required _i39.LearnModule module,
|
||||
required bool hasPractice,
|
||||
|
|
@ -2448,7 +2461,11 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
}) async {
|
||||
return replaceWith<dynamic>(Routes.learnLessonDetailView,
|
||||
arguments: LearnLessonDetailViewArguments(
|
||||
key: key, lesson: lesson, module: module, hasPractice: hasPractice),
|
||||
key: key,
|
||||
index: index,
|
||||
lesson: lesson,
|
||||
module: module,
|
||||
hasPractice: hasPractice),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
|
|
@ -2584,23 +2601,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return replaceWith<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithAssessmentView({
|
||||
_i37.Key? key,
|
||||
required Map<String, dynamic> data,
|
||||
|
|
@ -2634,23 +2634,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithArifPayView({
|
||||
_i37.Key? key,
|
||||
required String phone,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return replaceWith<dynamic>(Routes.arifPayView,
|
||||
arguments: ArifPayViewArguments(key: key, phone: phone),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithCourseCatalogView({
|
||||
_i37.Key? key,
|
||||
int? routerId,
|
||||
|
|
@ -2719,5 +2702,39 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return replaceWith<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithPaymentView({
|
||||
_i37.Key? key,
|
||||
required String phone,
|
||||
required _i46.LearnSubscription subscription,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return replaceWith<dynamic>(Routes.paymentView,
|
||||
arguments: PaymentViewArguments(
|
||||
key: key, phone: phone, subscription: subscription),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,14 +59,14 @@ class DefaultFirebaseOptions {
|
|||
|
||||
static const FirebaseOptions ios = FirebaseOptions(
|
||||
apiKey: 'AIzaSyDbaGD47oUJOyn9n3b0pbH6ozmbGyIlOKk',
|
||||
appId: '1:900714037062:ios:1caf8f24a4333b8e4e6f47',
|
||||
appId: '1:900714037062:ios:45b484d79222c3ab4e6f47',
|
||||
messagingSenderId: '900714037062',
|
||||
projectId: 'yimaru-academy-5e7e2',
|
||||
storageBucket: 'yimaru-academy-5e7e2.firebasestorage.app',
|
||||
androidClientId:
|
||||
'900714037062-4trqu7ln6en4kcm6gadk0uo01qijn1mk.apps.googleusercontent.com',
|
||||
iosClientId:
|
||||
'900714037062-35bg0hsou56hg37mbcbpiar9uti7tcku.apps.googleusercontent.com',
|
||||
iosBundleId: 'com.yimaru.lms.app',
|
||||
'900714037062-3qqf7urii8vg99id91nmmuvom3fm5c1u.apps.googleusercontent.com',
|
||||
iosBundleId: 'com.yimaru.lms.testapp',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:toastification/toastification.dart';
|
||||
import 'package:yimaru_app/app/app.bottomsheets.dart';
|
||||
import 'package:yimaru_app/app/app.dialogs.dart';
|
||||
|
|
@ -13,17 +14,22 @@ import 'firebase_options.dart';
|
|||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await setupLocator();
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
await locator<NotificationService>().initialize();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
setupDialogUi();
|
||||
setupBottomSheetUi();
|
||||
await SystemChrome.setPreferredOrientations(
|
||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
|
||||
runApp(
|
||||
EasyLocalization(
|
||||
supportedLocales: const [
|
||||
Locale('en'),
|
||||
Locale('አማ'),
|
||||
Locale(
|
||||
'am',
|
||||
),
|
||||
],
|
||||
path: 'assets/translations',
|
||||
startLocale: const Locale('en'),
|
||||
|
|
|
|||
|
|
@ -21,13 +21,39 @@ class Access {
|
|||
@JsonKey(name: 'progress_percent')
|
||||
final int? progressPercent;
|
||||
|
||||
const Access(
|
||||
{this.reason,
|
||||
this.totalCount,
|
||||
this.isCompleted,
|
||||
this.isAccessible,
|
||||
this.completedCount,
|
||||
this.progressPercent});
|
||||
@JsonKey(name: 'progress_percent_precise')
|
||||
final double? progressPercentPrecise;
|
||||
|
||||
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,
|
||||
double? 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ Access _$AccessFromJson(Map<String, dynamic> json) => Access(
|
|||
isAccessible: json['is_accessible'] as bool?,
|
||||
completedCount: (json['completed_count'] as num?)?.toInt(),
|
||||
progressPercent: (json['progress_percent'] as num?)?.toInt(),
|
||||
progressPercentPrecise:
|
||||
(json['progress_percent_precise'] as num?)?.toDouble(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AccessToJson(Access instance) => <String, dynamic>{
|
||||
|
|
@ -22,4 +24,5 @@ Map<String, dynamic> _$AccessToJson(Access instance) => <String, dynamic>{
|
|||
'is_accessible': instance.isAccessible,
|
||||
'completed_count': instance.completedCount,
|
||||
'progress_percent': instance.progressPercent,
|
||||
'progress_percent_precise': instance.progressPercentPrecise,
|
||||
};
|
||||
|
|
|
|||
19
lib/models/app_update.dart
Normal file
19
lib/models/app_update.dart
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'app_update.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class AppUpdate {
|
||||
@JsonKey(name: 'force_update')
|
||||
final bool? forceUpdate;
|
||||
|
||||
@JsonKey(name: 'update_available')
|
||||
final bool? updateAvailable;
|
||||
|
||||
const AppUpdate({this.forceUpdate, this.updateAvailable});
|
||||
|
||||
factory AppUpdate.fromJson(Map<String, dynamic> json) =>
|
||||
_$AppUpdateFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$AppUpdateToJson(this);
|
||||
}
|
||||
17
lib/models/app_update.g.dart
Normal file
17
lib/models/app_update.g.dart
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_update.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
AppUpdate _$AppUpdateFromJson(Map<String, dynamic> json) => AppUpdate(
|
||||
forceUpdate: json['force_update'] as bool?,
|
||||
updateAvailable: json['update_available'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AppUpdateToJson(AppUpdate instance) => <String, dynamic>{
|
||||
'force_update': instance.forceUpdate,
|
||||
'update_available': instance.updateAvailable,
|
||||
};
|
||||
|
|
@ -1,39 +1,25 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/module_progress.dart';
|
||||
|
||||
import 'access.dart';
|
||||
|
||||
part 'course_progress.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class CourseProgress {
|
||||
final String? level;
|
||||
final int? id;
|
||||
|
||||
final String? title;
|
||||
final String? name;
|
||||
|
||||
final String? description;
|
||||
final Access? access;
|
||||
|
||||
@JsonKey(name: 'is_locked')
|
||||
final bool? isLocked;
|
||||
final List<ModuleProgress>? modules;
|
||||
|
||||
@JsonKey(name: 'sub_course_id')
|
||||
final int? courseId;
|
||||
|
||||
@JsonKey(name: 'display_order')
|
||||
final int? displayOrder;
|
||||
|
||||
@JsonKey(name: 'progress_status')
|
||||
final String? progressStatus;
|
||||
|
||||
@JsonKey(name: 'progress_percentage')
|
||||
final double? progressPercentage;
|
||||
@JsonKey(name: 'program_id')
|
||||
final int? programId;
|
||||
|
||||
const CourseProgress(
|
||||
{this.level,
|
||||
this.title,
|
||||
this.isLocked,
|
||||
this.courseId,
|
||||
this.description,
|
||||
this.displayOrder,
|
||||
this.progressStatus,
|
||||
this.progressPercentage});
|
||||
{this.id, this.name, this.access, this.modules, this.programId});
|
||||
|
||||
factory CourseProgress.fromJson(Map<String, dynamic> json) =>
|
||||
_$CourseProgressFromJson(json);
|
||||
|
|
|
|||
|
|
@ -8,24 +8,22 @@ part of 'course_progress.dart';
|
|||
|
||||
CourseProgress _$CourseProgressFromJson(Map<String, dynamic> json) =>
|
||||
CourseProgress(
|
||||
level: json['level'] as String?,
|
||||
title: json['title'] as String?,
|
||||
isLocked: json['is_locked'] as bool?,
|
||||
courseId: (json['sub_course_id'] as num?)?.toInt(),
|
||||
description: json['description'] as String?,
|
||||
displayOrder: (json['display_order'] as num?)?.toInt(),
|
||||
progressStatus: json['progress_status'] as String?,
|
||||
progressPercentage: (json['progress_percentage'] as num?)?.toDouble(),
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
name: json['name'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
modules: (json['modules'] as List<dynamic>?)
|
||||
?.map((e) => ModuleProgress.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
programId: (json['program_id'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CourseProgressToJson(CourseProgress instance) =>
|
||||
<String, dynamic>{
|
||||
'level': instance.level,
|
||||
'title': instance.title,
|
||||
'description': instance.description,
|
||||
'is_locked': instance.isLocked,
|
||||
'sub_course_id': instance.courseId,
|
||||
'display_order': instance.displayOrder,
|
||||
'progress_status': instance.progressStatus,
|
||||
'progress_percentage': instance.progressPercentage,
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'access': instance.access,
|
||||
'modules': instance.modules,
|
||||
'program_id': instance.programId,
|
||||
};
|
||||
|
|
|
|||
17
lib/models/field_option.dart
Normal file
17
lib/models/field_option.dart
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'field_option.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class FieldOption {
|
||||
final String? code;
|
||||
|
||||
final String? label;
|
||||
|
||||
const FieldOption({this.code, this.label});
|
||||
|
||||
factory FieldOption.fromJson(Map<String, dynamic> json) =>
|
||||
_$FieldOptionFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$FieldOptionToJson(this);
|
||||
}
|
||||
18
lib/models/field_option.g.dart
Normal file
18
lib/models/field_option.g.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'field_option.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
FieldOption _$FieldOptionFromJson(Map<String, dynamic> json) => FieldOption(
|
||||
code: json['code'] as String?,
|
||||
label: json['label'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$FieldOptionToJson(FieldOption instance) =>
|
||||
<String, dynamic>{
|
||||
'code': instance.code,
|
||||
'label': instance.label,
|
||||
};
|
||||
|
|
@ -19,13 +19,32 @@ class LearnCourse {
|
|||
@JsonKey(name: 'program_id')
|
||||
final int? programId;
|
||||
|
||||
const LearnCourse(
|
||||
{this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.programId,
|
||||
this.sortOrder,
|
||||
this.description});
|
||||
const LearnCourse({
|
||||
this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.programId,
|
||||
this.sortOrder,
|
||||
this.description,
|
||||
});
|
||||
|
||||
LearnCourse copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
Access? access,
|
||||
int? sortOrder,
|
||||
int? programId,
|
||||
String? description,
|
||||
}) {
|
||||
return LearnCourse(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
access: access ?? this.access,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
programId: programId ?? this.programId,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnCourse.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnCourseFromJson(json);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,28 @@ class LearnLesson {
|
|||
this.description,
|
||||
});
|
||||
|
||||
LearnLesson copyWith({
|
||||
int? id,
|
||||
String? title,
|
||||
int? moduleId,
|
||||
int? sortOrder,
|
||||
Access? access,
|
||||
String? videoUrl,
|
||||
String? thumbnail,
|
||||
String? description,
|
||||
}) {
|
||||
return LearnLesson(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
access: access ?? this.access,
|
||||
videoUrl: videoUrl ?? this.videoUrl,
|
||||
moduleId: moduleId ?? this.moduleId,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
thumbnail: thumbnail ?? this.thumbnail,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnLesson.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnLessonFromJson(json);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,38 @@ class LearnModule {
|
|||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
const LearnModule(
|
||||
{this.id,
|
||||
this.icon,
|
||||
this.name,
|
||||
this.access,
|
||||
this.courseId,
|
||||
this.sortOrder,
|
||||
this.programId,
|
||||
this.description});
|
||||
const LearnModule({
|
||||
this.id,
|
||||
this.icon,
|
||||
this.name,
|
||||
this.access,
|
||||
this.courseId,
|
||||
this.sortOrder,
|
||||
this.programId,
|
||||
this.description,
|
||||
});
|
||||
|
||||
LearnModule copyWith({
|
||||
int? id,
|
||||
String? icon,
|
||||
String? name,
|
||||
int? courseId,
|
||||
Access? access,
|
||||
int? programId,
|
||||
int? sortOrder,
|
||||
String? description,
|
||||
}) {
|
||||
return LearnModule(
|
||||
id: id ?? this.id,
|
||||
icon: icon ?? this.icon,
|
||||
name: name ?? this.name,
|
||||
access: access ?? this.access,
|
||||
courseId: courseId ?? this.courseId,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
programId: programId ?? this.programId,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnModule.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnModuleFromJson(json);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,29 @@ class LearnProgram {
|
|||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
const LearnProgram(
|
||||
{this.id, this.name, this.access, this.sortOrder, this.description});
|
||||
const LearnProgram({
|
||||
this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.sortOrder,
|
||||
this.description,
|
||||
});
|
||||
|
||||
LearnProgram copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
Access? access,
|
||||
int? sortOrder,
|
||||
String? description,
|
||||
}) {
|
||||
return LearnProgram(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
access: access ?? this.access,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnProgram.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnProgramFromJson(json);
|
||||
|
|
|
|||
24
lib/models/lesson_progress.dart
Normal file
24
lib/models/lesson_progress.dart
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
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,
|
||||
};
|
||||
|
|
@ -37,25 +37,28 @@ class User {
|
|||
@JsonKey(name: 'profile_completed')
|
||||
final bool? profileCompleted;
|
||||
|
||||
@JsonKey(name: 'subscription_status')
|
||||
final String? subscriptionStatus;
|
||||
|
||||
@JsonKey(name: 'profile_picture_url')
|
||||
final String? profilePicture;
|
||||
|
||||
const User({
|
||||
this.email,
|
||||
this.region,
|
||||
this.gender,
|
||||
this.userId,
|
||||
this.country,
|
||||
this.lastName,
|
||||
this.birthday,
|
||||
this.firstName,
|
||||
this.occupation,
|
||||
this.accessToken,
|
||||
this.refreshToken,
|
||||
this.profilePicture,
|
||||
this.userInfoLoaded,
|
||||
this.profileCompleted,
|
||||
});
|
||||
const User(
|
||||
{this.email,
|
||||
this.region,
|
||||
this.gender,
|
||||
this.userId,
|
||||
this.country,
|
||||
this.lastName,
|
||||
this.birthday,
|
||||
this.firstName,
|
||||
this.occupation,
|
||||
this.accessToken,
|
||||
this.refreshToken,
|
||||
this.profilePicture,
|
||||
this.userInfoLoaded,
|
||||
this.profileCompleted,
|
||||
this.subscriptionStatus});
|
||||
|
||||
User copyWith(
|
||||
{int? userId,
|
||||
|
|
@ -71,7 +74,8 @@ class User {
|
|||
String? refreshToken,
|
||||
bool? userInfoLoaded,
|
||||
bool? profileCompleted,
|
||||
String? profilePicture}) =>
|
||||
String? profilePicture,
|
||||
String? subscriptionStatus}) =>
|
||||
User(
|
||||
email: email ?? this.email,
|
||||
userId: userId ?? this.userId,
|
||||
|
|
@ -86,7 +90,8 @@ class User {
|
|||
refreshToken: refreshToken ?? this.refreshToken,
|
||||
userInfoLoaded: userInfoLoaded ?? this.userInfoLoaded,
|
||||
profilePicture: profilePicture ?? this.profilePicture,
|
||||
profileCompleted: profileCompleted ?? this.profileCompleted);
|
||||
profileCompleted: profileCompleted ?? this.profileCompleted,
|
||||
subscriptionStatus: subscriptionStatus ?? this.subscriptionStatus);
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ User _$UserFromJson(Map<String, dynamic> json) => User(
|
|||
profilePicture: json['profile_picture_url'] as String?,
|
||||
userInfoLoaded: json['userInfoLoaded'] as bool?,
|
||||
profileCompleted: json['profile_completed'] as bool?,
|
||||
subscriptionStatus: json['subscription_status'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
||||
|
|
@ -37,5 +38,6 @@ Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
|||
'access_token': instance.accessToken,
|
||||
'refresh_token': instance.refreshToken,
|
||||
'profile_completed': instance.profileCompleted,
|
||||
'subscription_status': instance.subscriptionStatus,
|
||||
'profile_picture_url': instance.profilePicture,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import 'package:dio/dio.dart';
|
||||
import 'package:yimaru_app/models/app_update.dart';
|
||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||
import 'package:yimaru_app/models/learn_practice.dart';
|
||||
import 'package:yimaru_app/models/learn_program.dart';
|
||||
import 'package:yimaru_app/models/assessment_question.dart';
|
||||
import 'package:yimaru_app/models/course_catalog.dart';
|
||||
import 'package:yimaru_app/models/course_lesson.dart';
|
||||
import 'package:yimaru_app/models/refresh_object.dart';
|
||||
import 'package:yimaru_app/models/user.dart';
|
||||
import 'package:yimaru_app/services/dio_service.dart';
|
||||
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||
|
|
@ -12,12 +14,14 @@ import 'package:yimaru_app/ui/common/app_constants.dart';
|
|||
import '../app/app.locator.dart';
|
||||
import '../models/course_module.dart';
|
||||
import '../models/course_unit.dart';
|
||||
import '../models/field_option.dart';
|
||||
import '../models/learn_course.dart';
|
||||
import '../models/learn_module.dart';
|
||||
import '../models/learn_question.dart';
|
||||
import '../models/learn_subscription.dart';
|
||||
import '../models/assessment.dart';
|
||||
import '../models/learn_subscription_request.dart';
|
||||
import '../models/progress_summary.dart';
|
||||
import '../ui/common/enmus.dart';
|
||||
|
||||
class ApiService {
|
||||
|
|
@ -108,6 +112,34 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Apple auth
|
||||
Future<Map<String, dynamic>> appleAuth(Map<String, dynamic> data) async {
|
||||
try {
|
||||
Response response = await _service.dio.post(
|
||||
'$kBaseUrl/$kAppleAuthUrl',
|
||||
data: data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'message': 'Logged in successfully',
|
||||
'data': User.fromJson(response.data['data']),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': '${response.data['message']}, ${response.data['error']}'
|
||||
};
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': e.response?.data.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Verify otp
|
||||
Future<Map<String, dynamic>> verifyOtp(Map<String, dynamic> data) async {
|
||||
try {
|
||||
|
|
@ -379,6 +411,222 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Get educational levels
|
||||
Future<List<FieldOption>> getEducationalLevels() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=education_level');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['education_level'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get ethiopia regions
|
||||
Future<List<FieldOption>> getEthiopiaRegions() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=ethiopia_regions');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['ethiopia_regions'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get language challenges
|
||||
Future<List<FieldOption>> getLanguageChallenges() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=language_challange');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['language_challange'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get occupations
|
||||
Future<List<FieldOption>> getOccupations() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=occupation');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['occupation'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get age group
|
||||
Future<List<FieldOption>> getAgeGroups() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=age_group');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['age_group'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get countries
|
||||
Future<List<FieldOption>> getCountries() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=country');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['country'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get topics
|
||||
Future<List<FieldOption>> getTopics() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=favourite_topic');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['favourite_topic'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get topics
|
||||
Future<List<FieldOption>> getLanguageGoals() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=language_goal');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['language_goal'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get learning goal
|
||||
Future<List<FieldOption>> getLearningGoals() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=learning_goal');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['learning_goal'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get assessment questions
|
||||
Future<List<AssessmentQuestion>> getAssessmentQuestions(int id) async {
|
||||
try {
|
||||
|
|
@ -403,6 +651,34 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Complete profile
|
||||
Future<Map<String, dynamic>> refreshObject(Map<String, dynamic> data) async {
|
||||
try {
|
||||
Response response = await _service.dio.post(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFilesUrl/$kRefreshUrl',
|
||||
data: data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'message': 'Operation successful',
|
||||
'data': RefreshObject.fromJson(response.data['data'])
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': 'Unknown Error Occurred'
|
||||
};
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': e.response?.data.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Learn learn programs
|
||||
Future<List<LearnProgram>> getLearnPrograms() async {
|
||||
try {
|
||||
|
|
@ -623,6 +899,30 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Get progress summary
|
||||
Future<List<ProgressSummary>> getProgressSummary() async {
|
||||
try {
|
||||
List<ProgressSummary> summaries = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kLmsUrl/$kProgressSummary');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['programs'] as List;
|
||||
summaries = decodedData.map(
|
||||
(e) {
|
||||
return ProgressSummary.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return summaries;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Complete lesson
|
||||
Future<Map<String, dynamic>> completeLearnPractice(int id) async {
|
||||
try {
|
||||
|
|
@ -680,6 +980,32 @@ class ApiService {
|
|||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kPaymentsUrl/$kSubscribeUrl',
|
||||
data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'message': 'Subscription successful!',
|
||||
'data': LearnSubscriptionRequest.fromJson(response.data['data']),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': 'Unknown Error Occurred'
|
||||
};
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': e.response?.data.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Verify subscription
|
||||
Future<Map<String, dynamic>> verifySubscription(int id) async {
|
||||
try {
|
||||
Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kPaymentsUrl/$kVerifySubscriptionUrl/$id');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'message': 'Lesson completed',
|
||||
|
|
@ -795,4 +1121,29 @@ class ApiService {
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Check update
|
||||
Future<Map<String, dynamic>> checkUpdate(Map<String, dynamic> data) async {
|
||||
try {
|
||||
Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kAppUrl/$kApiVersionUrl/$kCheckUrl',
|
||||
data: data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'data': AppUpdate.fromJson(response.data['data']),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
};
|
||||
}
|
||||
} on DioException {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
lib/services/apple_auth_service.dart
Normal file
36
lib/services/apple_auth_service.dart
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
class AppleAuthService with ListenableServiceMixin {
|
||||
AuthorizationCredentialAppleID? _appleCredential;
|
||||
|
||||
AuthorizationCredentialAppleID? get appleCredential => _appleCredential;
|
||||
|
||||
AppleAuthService() {
|
||||
listenToReactiveValues([_appleCredential]);
|
||||
}
|
||||
|
||||
bool get isSupported => Platform.isIOS;
|
||||
|
||||
Future<void> appleAuth() async {
|
||||
if (!isSupported) {
|
||||
throw UnsupportedError('Apple Sign-In is only available on iOS.');
|
||||
}
|
||||
|
||||
_appleCredential = await SignInWithApple.getAppleIDCredential(
|
||||
scopes: [
|
||||
AppleIDAuthorizationScopes.email,
|
||||
AppleIDAuthorizationScopes.fullName,
|
||||
],
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void logout() {
|
||||
_appleCredential = null;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
@ -90,6 +101,8 @@ class AuthenticationService with ListenableServiceMixin {
|
|||
await _secureService.setBool('userInfoLoaded', true);
|
||||
await _secureService.setBool(
|
||||
'profileCompleted', data.profileCompleted ?? false);
|
||||
await _secureService.setString(
|
||||
'subscriptionStatus', data.subscriptionStatus ?? '');
|
||||
await _secureService.setString('email', data.email ?? '');
|
||||
await _secureService.setString('region', data.region ?? '');
|
||||
await _secureService.setString('gender', data.gender ?? '');
|
||||
|
|
@ -113,6 +126,7 @@ class AuthenticationService with ListenableServiceMixin {
|
|||
accessToken: _user?.accessToken,
|
||||
refreshToken: _user?.refreshToken,
|
||||
profileCompleted: data.profileCompleted,
|
||||
subscriptionStatus: data.subscriptionStatus,
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
|
|
@ -169,19 +183,27 @@ class AuthenticationService with ListenableServiceMixin {
|
|||
userInfoLoaded: await _secureService.getBool('userInfoLoaded'),
|
||||
profilePicture: await _secureService.getString('profilePicture'),
|
||||
profileCompleted: await _secureService.getBool('profileCompleted'),
|
||||
subscriptionStatus: await _secureService.getString('subscriptionStatus'),
|
||||
);
|
||||
return _user;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class DioService {
|
|||
DioService() {
|
||||
_dio.options
|
||||
..baseUrl = kBaseUrl
|
||||
..connectTimeout = const Duration(seconds: 5)
|
||||
..connectTimeout = const Duration(seconds: 10)
|
||||
..receiveTimeout = const Duration(seconds: 15);
|
||||
|
||||
_dio.interceptors.add(
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final Dio _dio = Dio();
|
||||
|
||||
// Image downloader
|
||||
Future<String> downloader(String? networkImage) async {
|
||||
late File image;
|
||||
Future<String?> downloader(String? networkImage) async {
|
||||
try {
|
||||
File? image;
|
||||
|
||||
late String profileImage;
|
||||
String? profileImage = networkImage;
|
||||
|
||||
final Directory appDir = await getApplicationDocumentsDirectory();
|
||||
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;
|
||||
}
|
||||
|
||||
if (profileImage != null) {
|
||||
final Response profileImageResponse = await _dio.get<List<int>>(
|
||||
profileImage,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
);
|
||||
final localImagePath = join(appDir.path, 'profile.jpg');
|
||||
image = File(localImagePath);
|
||||
image.writeAsBytes(profileImageResponse.data);
|
||||
|
||||
return image.path;
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Response profileImageResponse = await _service.dio.get(
|
||||
profileImage,
|
||||
options: Options(
|
||||
followRedirects: false,
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
);
|
||||
final imageName = basename(networkImage ?? '');
|
||||
final localImagePath = join(appDir.path, imageName);
|
||||
image = File(localImagePath);
|
||||
image.writeAsBytes(profileImageResponse.data);
|
||||
|
||||
return image.path;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,51 @@
|
|||
import 'package:battery_plus/battery_plus.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:in_app_update/in_app_update.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:storage_info/storage_info.dart';
|
||||
import 'package:yimaru_app/app/app.locator.dart';
|
||||
import 'package:yimaru_app/models/app_update.dart';
|
||||
import 'package:yimaru_app/services/status_checker_service.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../ui/common/ui_helpers.dart';
|
||||
import 'api_service.dart';
|
||||
|
||||
class InAppUpdateService {
|
||||
// Dependency Injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
Future<int> getBatteryLevel() async {
|
||||
final battery = Battery();
|
||||
final batteryLevel = await battery.batteryLevel;
|
||||
return batteryLevel;
|
||||
}
|
||||
|
||||
Future<int> getAvailableStorage() async {
|
||||
try {
|
||||
final availableStorage =
|
||||
await StorageInfo().getStorageFreeSpace(SpaceUnit.Bytes);
|
||||
return availableStorage.toInt(); // Convert GB to bytes
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
final _statusCheckerService = locator<StatusCheckerService>();
|
||||
|
||||
Future<void> checkForUpdate() async {
|
||||
const requiredStorage = 500 * 1024 * 1024;
|
||||
|
||||
final batteryLevel =
|
||||
await getBatteryLevel(); // Implement getBatteryLevel function
|
||||
final int storageAvailable =
|
||||
await getAvailableStorage(); // Implement getAvailableStorage
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
// KewedeConst().showErrorToast(
|
||||
// 'Unable to update app, please charge your phone & free up space.');
|
||||
} else if (batteryLevel < 20) {
|
||||
// KewedeConst()
|
||||
// .showErrorToast('Unable to update app, please charge your phone.');
|
||||
} else if (storageAvailable < requiredStorage) {
|
||||
// KewedeConst()
|
||||
// .showErrorToast('Unable to update app, please free up space.');
|
||||
}
|
||||
// Show user-friendly message explaining why update failed and suggesting solutions (e.g., charge device, free up space)
|
||||
}
|
||||
try {
|
||||
final info = await InAppUpdate.checkForUpdate();
|
||||
if (info.updateAvailability == UpdateAvailability.updateAvailable) {
|
||||
AppUpdateResult result = await InAppUpdate.performImmediateUpdate();
|
||||
if (result == AppUpdateResult.userDeniedUpdate) {
|
||||
showErrorToast('An update is required to continue using this app.');
|
||||
_navigationService.back();
|
||||
final String version = await _statusCheckerService.getAppVersion();
|
||||
|
||||
if (version != info.availableVersionCode.toString()) {
|
||||
Map<String, dynamic> data = {
|
||||
'platform': 'ANDROID',
|
||||
'version_code': info.availableVersionCode
|
||||
};
|
||||
Map<String, dynamic> response = await _apiService.checkUpdate(data);
|
||||
|
||||
if (response['status'] == ResponseStatus.success) {
|
||||
AppUpdate update = response['data'] as AppUpdate;
|
||||
if (update.updateAvailable ?? false) {
|
||||
if (update.forceUpdate ?? false) {
|
||||
AppUpdateResult result =
|
||||
await InAppUpdate.performImmediateUpdate();
|
||||
if (result == AppUpdateResult.userDeniedUpdate) {
|
||||
showErrorToast(
|
||||
'An update is required to continue using this app.');
|
||||
_navigationService.back();
|
||||
}
|
||||
} else {
|
||||
await InAppUpdate.startFlexibleUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ... rest of your update logic ...
|
||||
} on PlatformException {
|
||||
// Handle specific error code for better user experience and potentially different error messages for each issue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
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';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
import '../models/access.dart';
|
||||
import '../models/learn_course.dart';
|
||||
import '../models/learn_lesson.dart';
|
||||
import '../models/learn_module.dart';
|
||||
import '../models/learn_program.dart';
|
||||
import '../models/progress_summary.dart';
|
||||
import 'api_service.dart';
|
||||
|
||||
class LearnService with ListenableServiceMixin {
|
||||
|
|
@ -13,9 +19,25 @@ class LearnService with ListenableServiceMixin {
|
|||
|
||||
// Initialization
|
||||
learnService() {
|
||||
listenToReactiveValues([_programs, _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 = [];
|
||||
|
||||
|
|
@ -36,6 +58,21 @@ class LearnService with ListenableServiceMixin {
|
|||
|
||||
List<LearnLesson> get lessons => _lessons;
|
||||
|
||||
|
||||
|
||||
// Learn programs
|
||||
Future<String?> refreshObject(String url) async {
|
||||
Map<String, dynamic> data = {'reference': url};
|
||||
Map<String, dynamic> response = await _apiService.refreshObject(data);
|
||||
|
||||
if (response['status'] == ResponseStatus.success) {
|
||||
RefreshObject object = response['data'] as RefreshObject;
|
||||
|
||||
return object.url ?? '';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Learn programs
|
||||
Future<void> getLearnPrograms() async {
|
||||
_programs = await _apiService.getLearnPrograms();
|
||||
|
|
@ -63,4 +100,118 @@ class LearnService with ListenableServiceMixin {
|
|||
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Learn progress
|
||||
Future<void> getLearnProgress() async {
|
||||
final summaries = await _apiService.getProgressSummary();
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
LearnModule? getLearnModuleById(int id) {
|
||||
try {
|
||||
return _modules.firstWhere((e) => e.id == id);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
LearnCourse? getLearnCourseById(int id) {
|
||||
try {
|
||||
return _courses.firstWhere((e) => e.id == id);
|
||||
} catch (_) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class LocalizationService with ListenableServiceMixin {
|
|||
Map<String, dynamic> get selectedLanguage => _selectedLanguage;
|
||||
|
||||
final List<Map<String, dynamic>> _languages = [
|
||||
{'code': 'አማ', 'language': 'አማርኛ'},
|
||||
{'code': 'am', 'language': 'አማርኛ'},
|
||||
{'code': 'en', 'language': 'English'},
|
||||
];
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ class LocalizationService with ListenableServiceMixin {
|
|||
required Map<String, dynamic> title}) async {
|
||||
_selectedLanguage = title;
|
||||
|
||||
if (title['code'] == 'አማ') {
|
||||
if (title['code'] == 'am') {
|
||||
await setAmharicLanguage(context);
|
||||
} else {
|
||||
await setEnglishLanguage(context);
|
||||
|
|
@ -50,17 +50,15 @@ class LocalizationService with ListenableServiceMixin {
|
|||
|
||||
if (language == 'en') {
|
||||
_selectedLanguage = {'code': 'en', 'language': 'English'};
|
||||
|
||||
} else {
|
||||
_selectedLanguage = {'code': 'አማ', 'language': 'አማርኛ'};
|
||||
_selectedLanguage = {'code': 'am', 'language': 'አማርኛ'};
|
||||
}
|
||||
notifyListeners();
|
||||
print('SELECTED LANGUAGE: $language $_selectedLanguage');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> setAmharicLanguage(BuildContext context) async {
|
||||
await context.setLocale(const Locale('አማ'));
|
||||
await _secureService.setString('language', 'አማ');
|
||||
await context.setLocale(const Locale('am'));
|
||||
await _secureService.setString('language', 'am');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class NotificationService {
|
|||
}
|
||||
|
||||
Future<void> updateFCMToken() async {
|
||||
print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
||||
// print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
||||
_messaging.onTokenRefresh.listen((newToken) {
|
||||
// updateTokenOnServer(newToken);
|
||||
});
|
||||
|
|
|
|||
115
lib/services/onboarding_service.dart
Normal file
115
lib/services/onboarding_service.dart
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/field_option.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
import 'api_service.dart';
|
||||
|
||||
class OnboardingService with ListenableServiceMixin {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
// Initialization
|
||||
learnService() {
|
||||
listenToReactiveValues([
|
||||
_topics,
|
||||
_regions,
|
||||
_ageGroups,
|
||||
_countries,
|
||||
_challenges,
|
||||
_occupations,
|
||||
_learningGoals,
|
||||
_languageGoals,
|
||||
_educationalBackgrounds
|
||||
]);
|
||||
}
|
||||
|
||||
// Topics
|
||||
List<FieldOption> _topics = [];
|
||||
|
||||
List<FieldOption> get topics => _topics;
|
||||
|
||||
// Regions
|
||||
List<FieldOption> _regions = [];
|
||||
|
||||
List<FieldOption> get regions => _regions;
|
||||
|
||||
// Age groups
|
||||
List<FieldOption> _ageGroups = [];
|
||||
|
||||
List<FieldOption> get ageGroups => _ageGroups;
|
||||
|
||||
// Countries
|
||||
List<FieldOption> _countries = [];
|
||||
|
||||
List<FieldOption> get countries => _countries;
|
||||
|
||||
// Challenges
|
||||
List<FieldOption> _challenges = [];
|
||||
|
||||
List<FieldOption> get challenges => _challenges;
|
||||
|
||||
// Occupations
|
||||
List<FieldOption> _occupations = [];
|
||||
|
||||
List<FieldOption> get occupations => _occupations;
|
||||
|
||||
// Learning goals
|
||||
List<FieldOption> _learningGoals = [];
|
||||
|
||||
List<FieldOption> get learningGoals => _learningGoals;
|
||||
|
||||
// Language goals
|
||||
List<FieldOption> _languageGoals = [];
|
||||
|
||||
List<FieldOption> get languageGoals => _languageGoals;
|
||||
|
||||
// Educational backgrounds
|
||||
List<FieldOption> _educationalBackgrounds = [];
|
||||
|
||||
List<FieldOption> get educationalBackgrounds => _educationalBackgrounds;
|
||||
|
||||
// Onboarding fields
|
||||
Future<bool> getOnboardingFields() async {
|
||||
_topics = await _apiService.getTopics();
|
||||
|
||||
_ageGroups = await _apiService.getAgeGroups();
|
||||
|
||||
_countries = await _apiService.getCountries();
|
||||
|
||||
_occupations = await _apiService.getOccupations();
|
||||
|
||||
_regions = await _apiService.getEthiopiaRegions();
|
||||
|
||||
_learningGoals = await _apiService.getLearningGoals();
|
||||
|
||||
_languageGoals = await _apiService.getLanguageGoals();
|
||||
|
||||
_challenges = await _apiService.getLanguageChallenges();
|
||||
|
||||
_educationalBackgrounds = await _apiService.getEducationalLevels();
|
||||
notifyListeners();
|
||||
|
||||
if (_topics.isNotEmpty &&
|
||||
_ageGroups.isNotEmpty &&
|
||||
_countries.isNotEmpty &&
|
||||
_occupations.isNotEmpty &&
|
||||
_regions.isNotEmpty &&
|
||||
_learningGoals.isNotEmpty &&
|
||||
_challenges.isNotEmpty &&
|
||||
_educationalBackgrounds.isNotEmpty) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Profile detail fields
|
||||
Future<void> getProfileDetailFields() async {
|
||||
_countries = await _apiService.getCountries();
|
||||
|
||||
_occupations = await _apiService.getOccupations();
|
||||
|
||||
_regions = await _apiService.getEthiopiaRegions();
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:yimaru_app/services/secure_storage_service.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
|
|
@ -25,4 +26,16 @@ class StatusCheckerService {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get app version
|
||||
Future<String> getAppVersion() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
String version = packageInfo.version; // e.g. 1.0.0
|
||||
String buildNumber = packageInfo.buildNumber; // version code
|
||||
|
||||
print("Version: $version");
|
||||
print("Build Number: $buildNumber");
|
||||
return buildNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
// Endpoints
|
||||
String kBaseUrl = 'https://api.yimaruacademy.com';
|
||||
|
||||
String kLmsUrl = 'lms';
|
||||
|
||||
String kAppUrl = 'app';
|
||||
|
||||
String kApiUrl = 'api';
|
||||
|
||||
String kUnitsUrl = 'units';
|
||||
|
||||
String kCheckUrl = 'check';
|
||||
|
||||
String kFilesUrl = 'files';
|
||||
|
||||
String kApiVersionUrl = 'v1';
|
||||
|
||||
String kLevelsUrl = 'levels';
|
||||
|
|
@ -15,6 +23,8 @@ String kModulesUrl = 'modules';
|
|||
|
||||
String kLessonsUrl = 'lessons';
|
||||
|
||||
String kVersionUrl = 'version';
|
||||
|
||||
String kProgramsUrl = 'programs';
|
||||
|
||||
String kRegisterUrl = 'register';
|
||||
|
|
@ -25,13 +35,15 @@ String kCompleteUrl = 'complete';
|
|||
|
||||
String kPaymentsUrl = 'payments';
|
||||
|
||||
String kExamPrepUrl = 'exam-prep';
|
||||
|
||||
String kSubscribeUrl = 'subscribe';
|
||||
|
||||
String kPracticesUrl = 'practices';
|
||||
|
||||
String kQuestionsUrl = 'questions';
|
||||
|
||||
String kExamPrepUrl = 'exam-prep';
|
||||
String kRefreshUrl = 'refresh-url';
|
||||
|
||||
String kCoursePractice = 'by-owner';
|
||||
|
||||
|
|
@ -47,14 +59,20 @@ String kSubmodulesUrl = 'sub-modules';
|
|||
|
||||
String kSubcoursesUrl = 'sub-courses';
|
||||
|
||||
String kFieldOptions = 'field-options';
|
||||
|
||||
String kResetPassword = 'resetPassword';
|
||||
|
||||
String kVerifySubscriptionUrl = 'verify';
|
||||
|
||||
String kQuestionSetsUrl = 'question-sets';
|
||||
|
||||
String kRequestResetCode = 'sendResetCode';
|
||||
|
||||
String kSubcategoriesUrl = 'sub-categories';
|
||||
|
||||
String kProgressSummary = 'progress-summary';
|
||||
|
||||
String kPublishedVideos = 'videos/published';
|
||||
|
||||
String kCoursePracticeQuestions = 'questions';
|
||||
|
|
@ -83,6 +101,8 @@ String kLessonProgressUrl = 'api/v1/progress/videos';
|
|||
|
||||
String kGoogleAuthUrl = 'api/v1/auth/google/android';
|
||||
|
||||
String kAppleAuthUrl = 'api/v1/auth/apple';
|
||||
|
||||
String kCourseProgressUrl = 'api/v1/progress/courses';
|
||||
|
||||
String kAssessmentsUrl = 'api/v1/assessment/questions';
|
||||
|
|
@ -104,7 +124,6 @@ String kTelegramSupport = '@yimaruacademy2026';
|
|||
|
||||
String kTelegramSupportLink = 'https://t.me/yimaruacademy2026';
|
||||
|
||||
String kErrorUrl = 'https://yimaru.net/api/v1/payments/arifpay/error';
|
||||
String kErrorUrl = 'https://api.yimaruacademy.com/payment/error';
|
||||
|
||||
String kSuccessUrl =
|
||||
'https://api.yimaruacademy.com/api/v1/payments/arifpay/success';
|
||||
String kSuccessUrl = 'https://api.yimaruacademy.com/payment/success';
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ enum Voice { sample, recorded }
|
|||
enum ResponseStatus { success, failure }
|
||||
|
||||
// Login method
|
||||
enum LoginMethod { phone, email, google }
|
||||
enum LoginMethod { phone, email, google, apple }
|
||||
|
||||
// Sign-up method
|
||||
enum SignUpMethod { phone, email, google }
|
||||
enum SignUpMethod { phone, email, google, apple }
|
||||
|
||||
// Learn practice
|
||||
enum LearnPractices { course, module, lesson }
|
||||
|
|
@ -25,9 +25,13 @@ enum ProgressStatuses { pending, started, completed }
|
|||
// Duolingo types
|
||||
enum DuolingoAssessments { speaking, reading, writing, listening }
|
||||
|
||||
// Practice reason
|
||||
enum PracticeReason { course, module, lesson, previousModule, previousCourse }
|
||||
|
||||
// State object
|
||||
enum StateObjects {
|
||||
none,
|
||||
logout,
|
||||
courses,
|
||||
register,
|
||||
verifyOtp,
|
||||
|
|
@ -39,6 +43,8 @@ enum StateObjects {
|
|||
learnModules,
|
||||
learnCourses,
|
||||
profileImage,
|
||||
paymentStatus,
|
||||
profileDetail,
|
||||
learnPrograms,
|
||||
courseLessons,
|
||||
profileUpdate,
|
||||
|
|
@ -50,11 +56,14 @@ enum StateObjects {
|
|||
loginWithGoogle,
|
||||
loadLessonVideo,
|
||||
loadCourseVideo,
|
||||
progressSummary,
|
||||
requestResetCode,
|
||||
profileCompletion,
|
||||
learnSubscription,
|
||||
learnSubscriptions,
|
||||
loginWithApple,
|
||||
registerWithGoogle,
|
||||
registerWithApple,
|
||||
learnPracticeSample,
|
||||
learnPracticeAnswer,
|
||||
loginWithPhoneNumber,
|
||||
|
|
|
|||
|
|
@ -14,187 +14,32 @@ class CodegenLoader extends AssetLoader{
|
|||
return Future.value(mapLocales[locale.toString()]);
|
||||
}
|
||||
|
||||
static const Map<String,dynamic> _am = {
|
||||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
"dont_have_account": "መለያ የለዎትም? ይመዝገቡ",
|
||||
"email": "ኢሜይል",
|
||||
"password": "የይለፍ ቃል",
|
||||
"forgot_password": "የይለፍ ቃል ረሱ?",
|
||||
"cont": "ቀጥል",
|
||||
"register": "ይመዝገቡ",
|
||||
"login_with_google": "በጉግል ይግቡ",
|
||||
"or": "ወይም",
|
||||
"login_with_phone": "በስልክ ቁጥር ይግቡ",
|
||||
"create_account": "አዲስ መለያ ይፍጠሩ",
|
||||
"already_have_account": "መለያ አለዎት?",
|
||||
"login": " ይግቡ ",
|
||||
"register_with_google": "በጉግል ይመዝገቡ",
|
||||
"register_with_phone": "በስልክ ቁጥር ይመዝገቡ",
|
||||
"enter_phone_number": "የስልክ ቁጥርዎን ያስገቡ። የማረጋገጫ ኮድ እንልክልዎታለን።",
|
||||
"login_with_email": "በኢሜይል ይግቡ",
|
||||
"create_password": "የይለፍ ቃል ይፍጠሩ",
|
||||
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
||||
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
||||
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
||||
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||
"terms_of_services": "የአገልግሎት ውሎች",
|
||||
"and": "እና",
|
||||
"privacy_policy": "የግላዊነት ፖሊሲ",
|
||||
"register_with_email": "በኢሜል ይመዝገቡ",
|
||||
"verification_code": "የማረጋገጫ ኮድ",
|
||||
"resend_code": "ኮዱን እንደገና ላክ",
|
||||
"code_sent_to_phone": "ኮዱ ወደ ስልክ ቁጥርዎ ተልኳል",
|
||||
"code_sent_to_email": "ኮዱ ወደ ኢሜል ተልኳል",
|
||||
"resend_code_in": "ኮዱን እንደገና ለመላክ የቀረው ጊዜ",
|
||||
"reset_password": " የይለፍ ቃልን ይቀይሩ",
|
||||
"enter_email_reset_code": "ኢሜይልዎን ያስገቡ። የይለፍ ቃል መለወጫ ኮድ እንልክልዎታለን።",
|
||||
"please_wait": "እባክዎ ይጠብቁ",
|
||||
"reset_code_sent": "የመቀየሪያ ኮድ በተሳካ ሁኔታ ተልኳል",
|
||||
"reset_code": " የመቀየሪያ ኮድ ",
|
||||
"new_password": "አዲስ የይለፍ ቃል",
|
||||
"logged_in_successfully": "በተሳካ ሁኔታ ገብተዋል",
|
||||
"view_course": " ኮርሱን ይመልከቱ",
|
||||
"continue_learning": "መማርን ይቀጥሉ",
|
||||
"start_learning": "ትምህርትን ይጀምሩ",
|
||||
"completed": "ተጠናቋል",
|
||||
"take_practice": " ልምምድ ያድርጉ",
|
||||
"your_current_level": "የአሁኑ ደረጃዎ",
|
||||
"overall_progress": "አጠቃላይ እድገት",
|
||||
"great_work": "በርቱ! በጣም ጥሩ እየሰሩ ነው",
|
||||
"view_module": "ሞጁሉን ይመልከቱ",
|
||||
"progress": "እድገት",
|
||||
"keep_going": " ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": " በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"practice": "ልምምድ",
|
||||
"start": "ጀምር",
|
||||
"in_progress": "በሂደት ላይ",
|
||||
"hello": "ሰላም",
|
||||
"ready_to_learn": " ዛሬ እንግሊዝኛ ለመማር ተዘጋጅተዋል? ",
|
||||
"learn": "ይማሩ ",
|
||||
"course": "ኮርስ",
|
||||
"profile": " ፕሮፋይል ",
|
||||
"speaking_partner": "የንግግር ጓደኛ",
|
||||
"practice_what_you_learned": "አሁን የተማሩትን እንለማመድ",
|
||||
"practice_questions": "ጥቂት ጥያቄዎችን እጠይቃለሁ እና መልስ መስጠት ይችላሉ",
|
||||
"start_practice": "ልምምድ ጀምር",
|
||||
"almost_there": "ሊጨርሱ ተቃርበዋል",
|
||||
"finish_session": "እድገትዎን ለማየት ክፍለ ጊዜውን ያጠናቅቁ",
|
||||
"continue_practice": "ልምምዱን ይቀጥሉ",
|
||||
"end_session": "ክፍለ ጊዜውን ያብቁ ",
|
||||
"tap_start_to_listen": "ለማዳመጥ የጀምር ቁልፉን ይጫኑ",
|
||||
"practice_speaking": "ንግግርን ይለማመዱ",
|
||||
"tap_microphone": "ለመናገር ማይክሮፎኑን ይጫኑ",
|
||||
"reply": "እንደገና አዳምጥ",
|
||||
"cancel": "ይቅር",
|
||||
"you_are_speaking": "እየተናገሩ ነው",
|
||||
"practice_completed": "ልምምዱ ተጠናቅቋል",
|
||||
"great_improvement": "በዚህኛው በራስ መተማመንዎ ጨምሯል፤ ትልቅ መሻሻል ነው",
|
||||
"practice_again": "እንደገና ይለማመዱ",
|
||||
"conversation_review": "የንግግር ግምገማ",
|
||||
"result": "ውጤት",
|
||||
"quick_tip": "ጠቃሚ ምክር",
|
||||
"retry": "እንደገና ይሞክሩ",
|
||||
"completed_a1": "እንኳን ደስ አለዎት! A1 ደረጃን አጠናቅቀዋል",
|
||||
"analyzing_speaking": "የንግግር ችሎታዎን እየገመገምን ነው",
|
||||
"view_profile": "ፕሮፋይሎን ይመልከቱ ",
|
||||
"hi": "ሰላም",
|
||||
"edit_profile": "መገለጫ ያስተካክሉ",
|
||||
"first_name": "የመጀመሪያ ስም",
|
||||
"last_name": "የአባት ስም",
|
||||
"gender": "ፆታ",
|
||||
"male": "ወንድ",
|
||||
"female": "ሴት",
|
||||
"phone_number": "የስልክ ቁጥር",
|
||||
"country": "ሀገር",
|
||||
"region": "ክልል",
|
||||
"select_region": "ክልል ይምረጡ",
|
||||
"enter_your_city": "ከተማዎን ያስገቡ",
|
||||
"occupation": "የስራ መስክ",
|
||||
"select_occupation": "ሙያዎን ይምረጡ",
|
||||
"save_changes": "ለውጦችን ያስቀምጡ",
|
||||
"my_progress": "የእኔ እድገት",
|
||||
"track_your_achievement": "ስኬቶችዎን እና ተከታታይ የትምህርት ጉዞዎን ይከታተሉ",
|
||||
"account_and_privacy": "መለያ እና ግላዊነት",
|
||||
"manage_settings": "ቅንብሮችን እና የመተግበሪያ ምርጫዎችን ያስተዳድሩ",
|
||||
"support": "ድጋፍ",
|
||||
"get_help": "በስልክ ወይም በቴሌግራም እገዛ ያግኙ",
|
||||
"logout": "ውጣ",
|
||||
"app_settings": "የመተግበሪያ ቅንብሮች",
|
||||
"legal_and_information": "ሕጋዊ እና መረጃ",
|
||||
"change_language": "ቋንቋ ቀይር",
|
||||
"terms_and_conditions": "ውሎች እና ሁኔታዎች",
|
||||
"delete_account": "መለያ ሰርዝ",
|
||||
"language_preference": "የቋንቋ ምርጫ",
|
||||
"choose_your_language": "ለውጦችን አስቀምጥ",
|
||||
"switch_language_anytime": "ቋንቋዎችን በማንኛውም ጊዜ መቀየር ይችላሉ",
|
||||
"need_help": "እገዛ ይፈልጋሉ?",
|
||||
"call_support": "የስልክ ድጋፍ",
|
||||
"talk_with_support": "በቀጥታ ከድጋፍ ቡድናችን ጋር ይነጋገሩ",
|
||||
"telegram_support": "የቴሌግራም ድጋፍ",
|
||||
"chat_via_telegram": "በቴሌግራም በፍጥነት ይወያዩ",
|
||||
"call_our_support": "ከ3 ጠዋት እስከ 12 ማታ ድረስ የድጋፍ ቡድናችንን ይደውሉ",
|
||||
"tap_to_call": "ለመደወል ይንኩ",
|
||||
"join_telegram": "በቴሌግራም የይማሩ አካዳሚን ይቀላቀሉ",
|
||||
"connect_with_support_team": "ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
||||
"open_in_telegram": "በቴሌግራም ይክፈቱ",
|
||||
"search_for": "ፈልጉት",
|
||||
"current_level": "የአሁኑ ደረጃ",
|
||||
"keep_up_the_great_work": "በጣም ጥሩ እየሰራህ ነው! ቀጥልበት፣ አስደናቂ ነህ።",
|
||||
"no_practice_available": "ምንም ልምምድ አልተገኘም!",
|
||||
"begin_module_practice": "የሞጁሉን ልምምድ ጀምር",
|
||||
"lets_practice_lesson": "እንለማመድ",
|
||||
"lets_quickly_review": "በዚህ ሞጁል ውስጥ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"lets_practice_module": "አሁን የተማርከውን እንለማመድ!",
|
||||
"ask_you_few_actions": "ጥቂት ጥያቄዎችን እጠይቅሃለሁ፣ አንተም በተፈጥሮ መልስ ልትሰጥ ትችላለህ።",
|
||||
"begin_level_practice": "የደረጃ ልምምድን ጀምር",
|
||||
"lets_practice_course": "የኮርሱን ልምምድ እንለማመድ",
|
||||
"lets_quick_practice": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"speaking": "እየተናገረ ነው",
|
||||
"you_have_finished_practice": "ልምምድህን አጠናቀቅህ",
|
||||
"view_results": "ውጤቶቼን እይ",
|
||||
"sample_answer": "ናሙና መልስ",
|
||||
"your_answer": "መልስህ",
|
||||
"sound_confident": "በዚህ ጊዜ የበለጠ እምነት ያለህ ይመስላል — በጣም ጥሩ መሻሻል ነው!",
|
||||
"you_have_completed": "አያይ! አጠናቀቅህ",
|
||||
"yes": "አዎ",
|
||||
"no": "አይ",
|
||||
"want_to_quit": "ለመውጣት እርግጠኛ ነህ?",
|
||||
"required_field": "ይህ መስክ ያስፈልጋል",
|
||||
"enter_full_name": "ሙሉ ስምህን አስገባ",
|
||||
"invalid_email": "የማይሰራ የኢሜይል ቅርጸት",
|
||||
"phone_must_start_with": "የስልክ ቁጥር በ251 መጀመር አለበት",
|
||||
"phone_must_be": "የስልክ ቁጥር 12 አሃዞች መሆን አለበት",
|
||||
"what_should_we_call_you": "ምን ብለን እንጠራህ?",
|
||||
"name_for_personalization": "በመማር ጉዞህ ውስጥ ለግል ለማድረግ ስምህን እንጠቀማለን።",
|
||||
"choose_your_gender": "ጾታህን ምረጥ",
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።"
|
||||
};
|
||||
static const Map<String,dynamic> _en = {
|
||||
static const Map<String,dynamic> _en = {
|
||||
"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
"dont_have_account": "Don't have an account? Register",
|
||||
"dont_have_account": "Don't have an account?",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"forgot_password": "Forgot password?",
|
||||
"cont": "Continue",
|
||||
"register": "Register",
|
||||
"login_with_google": "Login with Google",
|
||||
"login_with_apple": "Login with Apple",
|
||||
"or": "Or",
|
||||
"login_with_phone": "Login with phone number",
|
||||
"create_account": "Create an account",
|
||||
"already_have_account": "Already have an account?",
|
||||
"login": "Login",
|
||||
"register_with_google": "Register with Google",
|
||||
"register_with_apple": "Register with Apple",
|
||||
"register_with_phone": "Register with phone number",
|
||||
"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_math": "password match",
|
||||
"password_match": "password match",
|
||||
"sign_up_agreement": "By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
||||
"terms_of_services": "Terms of Service",
|
||||
"and": "and",
|
||||
|
|
@ -298,7 +143,7 @@ static const Map<String,dynamic> _en = {
|
|||
"open_in_telegram": "Open in Telegram",
|
||||
"search_for": "Search for",
|
||||
"current_level": "Current Level",
|
||||
"keep_up_the_great_work": "Keep up the great work! You\\'re doing amazing.",
|
||||
"keep_up_the_great_work": "Keep up the great work! You're doing amazing.",
|
||||
"no_practice_available": "No practice available!",
|
||||
"begin_module_practice": "Begin Module Practice",
|
||||
"lets_practice_lesson": "Let’s Practice",
|
||||
|
|
@ -326,7 +171,246 @@ static const Map<String,dynamic> _en = {
|
|||
"what_should_we_call_you": "What should we call you?",
|
||||
"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.",
|
||||
"educational_background": "What’s your current educational level?",
|
||||
"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.",
|
||||
"location": "Where are you from?",
|
||||
"select_country_region": "Select your country and region from the dropdown",
|
||||
"select_country": "Select country",
|
||||
"learning_goal": "Choose your learning goal.",
|
||||
"language_goal": "What’s your main goal for improving your English?",
|
||||
"your_goal": "Your goal helps us tailor your learning journey.",
|
||||
"write_your_goal": "Write your goal…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"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.",
|
||||
"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.",
|
||||
"skip": "Skip",
|
||||
"finish_level": "Finish Level",
|
||||
"likely_speaker": "You’re likely speaker of",
|
||||
"great_job": "Great Job! Here’s your next step to keep improving.",
|
||||
"lets_start_practice": "Let's start your practice",
|
||||
"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",
|
||||
"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};
|
||||
static const Map<String,dynamic> _am = {
|
||||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
"dont_have_account": "መለያ የለዎትም?",
|
||||
"email": "ኢሜይል",
|
||||
"password": "የይለፍ ቃል",
|
||||
"forgot_password": "የይለፍ ቃል ረሱ?",
|
||||
"cont": "ቀጥል",
|
||||
"register": "ይመዝገቡ",
|
||||
"login_with_google": "በጉግል ይግቡ",
|
||||
"login_with_apple": "በአፕል ይግቡ",
|
||||
"or": "ወይም",
|
||||
"login_with_phone": "በስልክ ቁጥር ይግቡ",
|
||||
"create_account": "አዲስ መለያ ይፍጠሩ",
|
||||
"already_have_account": "መለያ አለዎት?",
|
||||
"login": " ይግቡ ",
|
||||
"register_with_google": "በጉግል ይመዝገቡ",
|
||||
"register_with_apple": "በአፕል ይመዝገቡ",
|
||||
"register_with_phone": "በስልክ ቁጥር ይመዝገቡ",
|
||||
"enter_phone_number": "የስልክ ቁጥርዎን ያስገቡ። የማረጋገጫ ኮድ እንልክልዎታለን።",
|
||||
"login_with_email": "በኢሜይል ይግቡ",
|
||||
"create_password": "የይለፍ ቃል ይፍጠሩ",
|
||||
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
||||
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
||||
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
||||
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||
"terms_of_services": "የአገልግሎት ውሎች",
|
||||
"and": "እና",
|
||||
"privacy_policy": "የግላዊነት ፖሊሲ",
|
||||
"register_with_email": "በኢሜል ይመዝገቡ",
|
||||
"verification_code": "የማረጋገጫ ኮድ",
|
||||
"resend_code": "ኮዱን እንደገና ላክ",
|
||||
"code_sent_to_phone": "ኮዱ ወደ ስልክ ቁጥርዎ ተልኳል",
|
||||
"code_sent_to_email": "ኮዱ ወደ ኢሜል ተልኳል",
|
||||
"resend_code_in": "ኮዱን እንደገና ለመላክ የቀረው ጊዜ",
|
||||
"reset_password": " የይለፍ ቃልን ይቀይሩ",
|
||||
"enter_email_reset_code": "ኢሜይልዎን ያስገቡ። የይለፍ ቃል መለወጫ ኮድ እንልክልዎታለን።",
|
||||
"please_wait": "እባክዎ ይጠብቁ",
|
||||
"reset_code_sent": "የመቀየሪያ ኮድ በተሳካ ሁኔታ ተልኳል",
|
||||
"reset_code": " የመቀየሪያ ኮድ ",
|
||||
"new_password": "አዲስ የይለፍ ቃል",
|
||||
"logged_in_successfully": "በተሳካ ሁኔታ ገብተዋል",
|
||||
"view_course": " ኮርሱን ይመልከቱ",
|
||||
"continue_learning": "መማርን ይቀጥሉ",
|
||||
"start_learning": "ትምህርትን ይጀምሩ",
|
||||
"completed": "ተጠናቋል",
|
||||
"take_practice": "ልምምድ ያድርጉ",
|
||||
"your_current_level": "የአሁኑ ደረጃዎ",
|
||||
"overall_progress": "አጠቃላይ እድገት",
|
||||
"great_work": "በርቱ! በጣም ጥሩ እየሰሩ ነው",
|
||||
"view_module": "ሞጁሉን ይመልከቱ",
|
||||
"progress": "እድገት",
|
||||
"keep_going": "ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": "በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"practice": "ልምምድ",
|
||||
"start": "ጀምር",
|
||||
"in_progress": "በሂደት ላይ",
|
||||
"hello": "ሰላም",
|
||||
"ready_to_learn": " ዛሬ እንግሊዝኛ ለመማር ተዘጋጅተዋል? ",
|
||||
"learn": "ይማሩ ",
|
||||
"course": "ኮርስ",
|
||||
"profile": " ፕሮፋይል ",
|
||||
"speaking_partner": "የንግግር ጓደኛ",
|
||||
"practice_what_you_learned": "አሁን የተማሩትን እንለማመድ",
|
||||
"practice_questions": "ጥቂት ጥያቄዎችን እጠይቃለሁ እና መልስ መስጠት ይችላሉ",
|
||||
"start_practice": "ልምምድ ጀምር",
|
||||
"almost_there": "ሊጨርሱ ተቃርበዋል",
|
||||
"finish_session": "እድገትዎን ለማየት ክፍለ ጊዜውን ያጠናቅቁ",
|
||||
"continue_practice": "ልምምዱን ይቀጥሉ",
|
||||
"end_session": "ክፍለ ጊዜውን ያብቁ ",
|
||||
"tap_start_to_listen": "ለማዳመጥ የጀምር ቁልፉን ይጫኑ",
|
||||
"practice_speaking": "ንግግርን ይለማመዱ",
|
||||
"tap_microphone": "ለመናገር ማይክሮፎኑን ይጫኑ",
|
||||
"reply": "እንደገና አዳምጥ",
|
||||
"cancel": "ይቅር",
|
||||
"you_are_speaking": "እየተናገሩ ነው",
|
||||
"practice_completed": "ልምምዱ ተጠናቅቋል",
|
||||
"great_improvement": "በዚህኛው በራስ መተማመንዎ ጨምሯል፤ ትልቅ መሻሻል ነው",
|
||||
"practice_again": "እንደገና ይለማመዱ",
|
||||
"conversation_review": "የንግግር ግምገማ",
|
||||
"result": "ውጤት",
|
||||
"quick_tip": "ጠቃሚ ምክር",
|
||||
"retry": "እንደገና ይሞክሩ",
|
||||
"completed_a1": "እንኳን ደስ አለዎት! A1 ደረጃን አጠናቅቀዋል",
|
||||
"analyzing_speaking": "የንግግር ችሎታዎን እየገመገምን ነው",
|
||||
"view_profile": "ፕሮፋይሎን ይመልከቱ ",
|
||||
"hi": "ሰላም",
|
||||
"edit_profile": "መገለጫ ያስተካክሉ",
|
||||
"first_name": "የመጀመሪያ ስም",
|
||||
"last_name": "የአባት ስም",
|
||||
"gender": "ፆታ",
|
||||
"male": "ወንድ",
|
||||
"female": "ሴት",
|
||||
"phone_number": "የስልክ ቁጥር",
|
||||
"country": "ሀገር",
|
||||
"region": "ክልል",
|
||||
"select_region": "ክልል ይምረጡ",
|
||||
"enter_your_city": "ከተማዎን ያስገቡ",
|
||||
"occupation": "የስራ መስክ",
|
||||
"select_occupation": "ሙያዎን ይምረጡ",
|
||||
"save_changes": "ለውጦችን ያስቀምጡ",
|
||||
"my_progress": "የእኔ እድገት",
|
||||
"track_your_achievement": "ስኬቶችዎን እና ተከታታይ የትምህርት ጉዞዎን ይከታተሉ",
|
||||
"account_and_privacy": "መለያ እና ግላዊነት",
|
||||
"manage_settings": "ቅንብሮችን እና የመተግበሪያ ምርጫዎችን ያስተዳድሩ",
|
||||
"support": "ድጋፍ",
|
||||
"get_help": "በስልክ ወይም በቴሌግራም እገዛ ያግኙ",
|
||||
"logout": "ውጣ",
|
||||
"app_settings": "የመተግበሪያ ቅንብሮች",
|
||||
"legal_and_information": "ሕጋዊ እና መረጃ",
|
||||
"change_language": "ቋንቋ ቀይር",
|
||||
"terms_and_conditions": "ውሎች እና ሁኔታዎች",
|
||||
"delete_account": "መለያ ሰርዝ",
|
||||
"language_preference": "የቋንቋ ምርጫ",
|
||||
"choose_your_language": "ለውጦችን አስቀምጥ",
|
||||
"switch_language_anytime": "ቋንቋዎችን በማንኛውም ጊዜ መቀየር ይችላሉ",
|
||||
"need_help": "እገዛ ይፈልጋሉ?",
|
||||
"call_support": "የስልክ ድጋፍ",
|
||||
"talk_with_support": "በቀጥታ ከድጋፍ ቡድናችን ጋር ይነጋገሩ",
|
||||
"telegram_support": "የቴሌግራም ድጋፍ",
|
||||
"chat_via_telegram": "በቴሌግራም በፍጥነት ይወያዩ",
|
||||
"call_our_support": "ከ3 ጠዋት እስከ 12 ማታ ድረስ የድጋፍ ቡድናችንን ይደውሉ",
|
||||
"tap_to_call": "ለመደወል ይንኩ",
|
||||
"join_telegram": "በቴሌግራም የይማሩ አካዳሚን ይቀላቀሉ",
|
||||
"connect_with_support_team": "ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
||||
"open_in_telegram": "በቴሌግራም ይክፈቱ",
|
||||
"search_for": "ፈልጉት",
|
||||
"current_level": "የአሁኑ ደረጃ",
|
||||
"keep_up_the_great_work": "በጣም ጥሩ እየሰራህ ነው! ቀጥልበት፣ አስደናቂ ነህ።",
|
||||
"no_practice_available": "ምንም ልምምድ አልተገኘም!",
|
||||
"begin_module_practice": "የሞጁሉን ልምምድ ጀምር",
|
||||
"lets_practice_lesson": "እንለማመድ",
|
||||
"lets_quickly_review": "በዚህ ሞጁል ውስጥ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"lets_practice_module": "አሁን የተማርከውን እንለማመድ!",
|
||||
"ask_you_few_actions": "ጥቂት ጥያቄዎችን እጠይቅሃለሁ፣ አንተም በተፈጥሮ መልስ ልትሰጥ ትችላለህ።",
|
||||
"begin_level_practice": "የደረጃ ልምምድን ጀምር",
|
||||
"lets_practice_course": "የኮርሱን ልምምድ እንለማመድ",
|
||||
"lets_quick_review": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"speaking": "እየተናገረ ነው",
|
||||
"you_have_finished_practice": "ልምምድህን አጠናቀቅህ",
|
||||
"view_results": "ውጤቶቼን እይ",
|
||||
"sample_answer": "ናሙና መልስ",
|
||||
"your_answer": "መልስህ",
|
||||
"sound_confident": "በዚህ ጊዜ የበለጠ እምነት ያለህ ይመስላል — በጣም ጥሩ መሻሻል ነው!",
|
||||
"you_have_completed": "አያይ! አጠናቀቅህ",
|
||||
"yes": "አዎ",
|
||||
"no": "አይ",
|
||||
"want_to_quit": "ለመውጣት እርግጠኛ ነህ?",
|
||||
"required_field": "ይህ መስክ ያስፈልጋል",
|
||||
"enter_full_name": "ሙሉ ስምህን አስገባ",
|
||||
"invalid_email": "የማይሰራ የኢሜይል ቅርጸት",
|
||||
"phone_must_start_with": "የስልክ ቁጥር በ251 መጀመር አለበት",
|
||||
"phone_must_be": "የስልክ ቁጥር 12 አሃዞች መሆን አለበት",
|
||||
"what_should_we_call_you": "ምን ብለን እንጠራህ?",
|
||||
"name_for_personalization": "በመማር ጉዞህ ውስጥ ለግል ለማድረግ ስምህን እንጠቀማለን።",
|
||||
"choose_your_gender": "ጾታህን ምረጥ",
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"age_range": "በየትኛው የእድሜ ክልል ውስጥ ነህ?",
|
||||
"age_for_personalization": "በእድሜህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"educational_background": "አሁን ያለህ የትምህርት ደረጃ ምንድን ነው?",
|
||||
"education_for_personalization": "ይህ ትምህርቶችን ከልምድህ ጋር እንዲስማሙ ለማድረግ ይረዳናል።",
|
||||
"your_occupation": "ስራህ ምንድን ነው?",
|
||||
"occupation_for_personalization": "በስራህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"location": "ከየት ነህ?",
|
||||
"select_country_region": "አገርህን እና ክልልህን ከተቆልቋይ ዝርዝሩ ምረጥ",
|
||||
"select_country": "አገር ምረጥ",
|
||||
"learning_goal": "የመማር ዓላማህን ምረጥ",
|
||||
"language_goal": "እንግሊዝኛህን ለማሻሻል ዋና ዓላማህ ምንድን ነው?",
|
||||
"your_goal": "ዓላማህ የመማር ጉዞህን እንዲስማማ ለማድረግ ይረዳናል።",
|
||||
"write_your_goal": "ዓላማህን ጻፍ…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||
"skip": "ዝለል",
|
||||
"finish_level": "ደረጃውን አጠናቅቅ",
|
||||
"likely_speaker": "አንተ ምናልባት ተናጋሪ ነህ",
|
||||
"great_job": "በጣም ጥሩ ስራ! ለመሻሻል ቀጣዩ ደረጃህ ይኸው ነው።",
|
||||
"lets_start_practice": "ልምምድህን እንጀምር",
|
||||
"welcome_abroad": "እንኳን ደህና መጣህ",
|
||||
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
||||
"finish": "አጠናቅቅ",
|
||||
"finish_all_practice_lesson": "ይህን ልምምድ ለመውሰድ የቀድሞውን የትምህርት ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_module": "የሞጁሉን ልምምድ ለመውሰድ የትምህርት ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_course": "የኮርሱን ልምምድ ለመውሰድ የሞጁል ልምምዶችን ያጠናቅቁ",
|
||||
"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, Map<String,dynamic>> mapLocales = {"en": _en, "am": _am};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ abstract class LocaleKeys {
|
|||
static const cont = 'cont';
|
||||
static const register = 'register';
|
||||
static const login_with_google = 'login_with_google';
|
||||
static const login_with_apple = 'login_with_apple';
|
||||
static const or = 'or';
|
||||
static const login_with_phone = 'login_with_phone';
|
||||
static const create_account = 'create_account';
|
||||
static const already_have_account = 'already_have_account';
|
||||
static const login = 'login';
|
||||
static const register_with_google = 'register_with_google';
|
||||
static const register_with_apple = 'register_with_apple';
|
||||
static const register_with_phone = 'register_with_phone';
|
||||
static const enter_phone_number = 'enter_phone_number';
|
||||
static const login_with_email = 'login_with_email';
|
||||
|
|
@ -43,10 +45,10 @@ abstract class LocaleKeys {
|
|||
static const reset_code = 'reset_code';
|
||||
static const new_password = 'new_password';
|
||||
static const logged_in_successfully = 'logged_in_successfully';
|
||||
static const view_course = 'view_course';
|
||||
static const continue_learning = 'continue_learning';
|
||||
static const start_learning = 'start_learning';
|
||||
static const completed = 'completed';
|
||||
static const view_course = 'view_course';
|
||||
static const take_practice = 'take_practice';
|
||||
static const your_current_level = 'your_current_level';
|
||||
static const overall_progress = 'overall_progress';
|
||||
|
|
@ -136,7 +138,7 @@ abstract class LocaleKeys {
|
|||
static const ask_you_few_actions = 'ask_you_few_actions';
|
||||
static const begin_level_practice = 'begin_level_practice';
|
||||
static const lets_practice_course = 'lets_practice_course';
|
||||
static const lets_quick_practice = 'lets_quick_practice';
|
||||
static const lets_quick_review = 'lets_quick_review';
|
||||
static const speaking = 'speaking';
|
||||
static const you_have_finished_practice = 'you_have_finished_practice';
|
||||
static const view_results = 'view_results';
|
||||
|
|
@ -156,5 +158,45 @@ abstract class LocaleKeys {
|
|||
static const name_for_personalization = 'name_for_personalization';
|
||||
static const choose_your_gender = 'choose_your_gender';
|
||||
static const gender_for_personalization = 'gender_for_personalization';
|
||||
static const age_range = 'age_range';
|
||||
static const age_for_personalization = 'age_for_personalization';
|
||||
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 location = 'location';
|
||||
static const select_country_region = 'select_country_region';
|
||||
static const select_country = 'select_country';
|
||||
static const learning_goal = 'learning_goal';
|
||||
static const language_goal = 'language_goal';
|
||||
static const your_goal = 'your_goal';
|
||||
static const write_your_goal = 'write_your_goal';
|
||||
static const challenge_you_face = 'challenge_you_face';
|
||||
static const evey_one_has_strugle = 'evey_one_has_strugle';
|
||||
static const write_your_challenge = 'write_your_challenge';
|
||||
static const topic_interest = 'topic_interest';
|
||||
static const favourite_topic = 'favourite_topic';
|
||||
static const your_interest = 'your_interest';
|
||||
static const want_quick_assessment = 'want_quick_assessment';
|
||||
static const answer_quick_questions = 'answer_quick_questions';
|
||||
static const skip = 'skip';
|
||||
static const finish_level = 'finish_level';
|
||||
static const likely_speaker = 'likely_speaker';
|
||||
static const great_job = 'great_job';
|
||||
static const lets_start_practice = 'lets_start_practice';
|
||||
static const welcome_abroad = 'welcome_abroad';
|
||||
static const ready_to_explore = 'ready_to_explore';
|
||||
static const finish = 'finish';
|
||||
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 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';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class FormValidator {
|
|||
}
|
||||
|
||||
if (value.isEmpty) {
|
||||
return LocaleKeys.required_field.tr();
|
||||
return LocaleKeys.required_field.tr();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,13 @@ class AccountPrivacyView extends StackedView<AccountPrivacyViewModel> {
|
|||
|
||||
Widget _buildScaffoldWrapper(AccountPrivacyViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
body: _buildScaffoldContainer(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffoldContainer(AccountPrivacyViewModel viewModel) =>
|
||||
Container(
|
||||
decoration: bgDecoration,
|
||||
child: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(AccountPrivacyViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ class AccountPrivacyViewModel extends ReactiveViewModel {
|
|||
final _localizationService = locator<LocalizationService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[ _localizationService];
|
||||
List<ListenableServiceMixin> get listenableServices => [_localizationService];
|
||||
|
||||
// Languages
|
||||
Map<String, dynamic> get _selectedLanguage =>
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||
|
||||
import 'arif_pay_viewmodel.dart';
|
||||
|
||||
class ArifPayView extends StackedView<ArifPayViewModel> {
|
||||
final String phone;
|
||||
|
||||
const ArifPayView({Key? key, required this.phone}) : super(key: key);
|
||||
|
||||
void _pop(ArifPayViewModel viewModel) => viewModel.pop;
|
||||
|
||||
Future<void> _error() async {
|
||||
// await Navigator.pushNamed(context, AppRoutes.subscriptionErrorPage);
|
||||
// Navigation.pop();
|
||||
}
|
||||
|
||||
void _success() {
|
||||
// Navigation.navigateTo(
|
||||
// AppRoutes.subscriptionSuccessPage,
|
||||
// arguments: widget.body,
|
||||
// );
|
||||
}
|
||||
|
||||
@override
|
||||
void onViewModelReady(ArifPayViewModel viewModel) async {
|
||||
await viewModel.createLearnSubscriptionRequest(phone);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
ArifPayViewModel viewModelBuilder(BuildContext context) => ArifPayViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
ArifPayViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(ArifPayViewModel viewModel) =>
|
||||
Scaffold(body: _buildScaffoldState(viewModel));
|
||||
|
||||
Widget _buildScaffoldState(ArifPayViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnSubscription)
|
||||
? const PageLoadingIndicator()
|
||||
: _buildScaffold(viewModel);
|
||||
|
||||
Widget _buildScaffold(ArifPayViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(ArifPayViewModel viewModel) => InAppWebView(
|
||||
initialUrlRequest:
|
||||
URLRequest(url: WebUri(viewModel.request?.paymentUrl ?? '')),
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||
if (url
|
||||
.toString()
|
||||
.contains("https://checkout.arifpay.net/canceled")) {
|
||||
showErrorToast('Operation was cancelled');
|
||||
// _pop();
|
||||
} else if (url.toString().contains(kSuccessUrl)) {
|
||||
_success();
|
||||
} else if (url.toString().contains(kErrorUrl)) {
|
||||
showErrorToast('Operation was cancelled');
|
||||
// _pop();
|
||||
} else if (url.toString().contains("http://x.com/elonmusk/status/")) {
|
||||
_error();
|
||||
} else if (url.toString().contains(kErrorUrl)) {
|
||||
_error();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/learn_subscription_request.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
||||
class ArifPayViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn subscription request
|
||||
LearnSubscriptionRequest? _request;
|
||||
|
||||
LearnSubscriptionRequest? get request => _request;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn subscription
|
||||
Future<void> createLearnSubscriptionRequest(String phone) async =>
|
||||
await runBusyFuture(_createLearnSubscriptionRequest(phone),
|
||||
busyObject: StateObjects.learnSubscription);
|
||||
|
||||
Future<void> _createLearnSubscriptionRequest(String phone) 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'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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'];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
@ -52,9 +52,9 @@ class AssessmentView extends StackedView<AssessmentViewModel> {
|
|||
];
|
||||
|
||||
Widget _buildAssessmentIntroWrapper(AssessmentViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.assessments) || viewModel.assessments.isEmpty
|
||||
viewModel.busy(StateObjects.assessments)
|
||||
? _buildPageLoadingIndicator(viewModel)
|
||||
: _buildAssessmentIntro();
|
||||
: _buildAssessmentIntro(viewModel);
|
||||
|
||||
Widget _buildPageLoadingIndicator(AssessmentViewModel viewModel) =>
|
||||
AssessmentLoadingScreen(
|
||||
|
|
@ -64,7 +64,10 @@ class AssessmentView extends StackedView<AssessmentViewModel> {
|
|||
onPop: viewModel.assessments.isEmpty ? viewModel.pop : null,
|
||||
);
|
||||
|
||||
Widget _buildAssessmentIntro() => const AssessmentIntroScreen();
|
||||
Widget _buildAssessmentIntro(AssessmentViewModel viewModel) =>
|
||||
AssessmentIntroScreen(
|
||||
hasAssessments: viewModel.assessments.isNotEmpty,
|
||||
);
|
||||
|
||||
Widget _buildAssessment() => const AssessmentQuestionsScreen();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
|
@ -8,7 +10,9 @@ import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
|||
import '../assessment_viewmodel.dart';
|
||||
|
||||
class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||
const AssessmentIntroScreen({super.key});
|
||||
final bool hasAssessments;
|
||||
|
||||
const AssessmentIntroScreen({super.key, required this.hasAssessments});
|
||||
|
||||
Future<void> _next(AssessmentViewModel viewModel) async =>
|
||||
viewModel.setFirstAssessment();
|
||||
|
|
@ -27,11 +31,8 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
children: _buildScaffoldChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren(AssessmentViewModel viewModel) => [
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildExpandedBody(viewModel)
|
||||
];
|
||||
List<Widget> _buildScaffoldChildren(AssessmentViewModel viewModel) =>
|
||||
[_buildAppBar(viewModel), _buildExpandedBody(viewModel)];
|
||||
|
||||
Widget _buildExpandedBody(AssessmentViewModel viewModel) =>
|
||||
Expanded(child: _buildBodyWrapper(viewModel));
|
||||
|
|
@ -57,7 +58,7 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
);
|
||||
|
||||
List<Widget> _buildUpperColumnChildren(AssessmentViewModel viewModel) => [
|
||||
verticalSpaceMedium,
|
||||
verticalSpaceLarge,
|
||||
_buildTitle(),
|
||||
verticalSpaceSmall,
|
||||
_buildSubtitle(),
|
||||
|
|
@ -67,17 +68,19 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Want a quick assessment to know your English level?',
|
||||
LocaleKeys.want_quick_assessment.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Answer a few quick questions to help us understand your English proficiency.',
|
||||
LocaleKeys.answer_quick_questions.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
@ -87,7 +90,7 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren(AssessmentViewModel viewModel) => [
|
||||
_buildContinueButton(viewModel),
|
||||
if (hasAssessments) _buildContinueButton(viewModel),
|
||||
verticalSpaceSmall,
|
||||
_buildSkipButtonWrapper(viewModel)
|
||||
];
|
||||
|
|
@ -96,9 +99,9 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
CustomElevatedButton(
|
||||
height: 55,
|
||||
safe: false,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await _next(viewModel),
|
||||
);
|
||||
|
|
@ -111,9 +114,9 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
Widget _buildSkipButton(AssessmentViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Skip',
|
||||
borderRadius: 12,
|
||||
backgroundColor: kcWhite,
|
||||
text: LocaleKeys.skip.tr(),
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
onTap: () => viewModel.next(page: 3),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||
|
|
@ -134,8 +136,8 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
foregroundColor: kcWhite,
|
||||
text: viewModel.currentQuestionIndex ==
|
||||
viewModel.assessmentQuestions.length - 1
|
||||
? 'Finish Level'
|
||||
: 'Continue',
|
||||
? LocaleKeys.finish_level.tr()
|
||||
: LocaleKeys.cont.tr(),
|
||||
backgroundColor: viewModel.selectedAnswers
|
||||
.containsKey('${viewModel.currentQuestionIndex + 1}')
|
||||
? kcPrimaryColor
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
|
@ -25,18 +27,17 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
children: _buildScaffoldChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren(AssessmentViewModel viewModel) => [
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildExpandedBody(viewModel)
|
||||
];
|
||||
List<Widget> _buildScaffoldChildren(AssessmentViewModel viewModel) =>
|
||||
[_buildAppBar(viewModel), _buildExpandedBody(viewModel)];
|
||||
|
||||
Widget _buildAppBar(AssessmentViewModel viewModel) => LargeAppBar(
|
||||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(AssessmentViewModel viewModel) =>
|
||||
|
|
@ -75,13 +76,13 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
];
|
||||
|
||||
Widget _buildTitle(AssessmentViewModel viewModel) => Text(
|
||||
'You’re likely a ${viewModel.proficiencyLevel?.toUpperCase()} speaker!',
|
||||
'${LocaleKeys.likely_speaker.tr()} ${viewModel.proficiencyLevel?.toUpperCase()}',
|
||||
style: style25DG600,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildPrimarySubtitle() => Text(
|
||||
'Great Job! Here’s your next step to keep improving.',
|
||||
LocaleKeys.great_job.tr(),
|
||||
style: style14MG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -93,7 +94,7 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
'assets/icons/${viewModel.proficiencyLevel?.substring(0, 1).toLowerCase()}_${viewModel.proficiencyLevel?.substring(1).toLowerCase()}.svg');
|
||||
|
||||
Widget _buildSecondarySubtitle() => Text(
|
||||
'Let\'s start your practice',
|
||||
LocaleKeys.lets_start_practice.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -106,9 +107,9 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
Widget _buildContinueButton(AssessmentViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: () => viewModel.next(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
|
@ -42,18 +44,17 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
children: _buildScaffoldChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren(AssessmentViewModel viewModel) => [
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildExpandedBody(viewModel)
|
||||
];
|
||||
List<Widget> _buildScaffoldChildren(AssessmentViewModel viewModel) =>
|
||||
[_buildAppBar(viewModel), _buildExpandedBody(viewModel)];
|
||||
|
||||
Widget _buildAppBar(AssessmentViewModel viewModel) => LargeAppBar(
|
||||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(AssessmentViewModel viewModel) =>
|
||||
|
|
@ -92,7 +93,7 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
|
||||
Widget _buildTitle(AssessmentViewModel viewModel) => Text.rich(
|
||||
TextSpan(
|
||||
text: 'Welcome aboard',
|
||||
text: LocaleKeys.welcome_abroad.tr(),
|
||||
style: style25DG600,
|
||||
children: [
|
||||
TextSpan(
|
||||
|
|
@ -104,7 +105,7 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'You’re ready to explore your personalized lessons.',
|
||||
LocaleKeys.ready_to_explore.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
@ -116,9 +117,9 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
Widget _buildContinueButton(AssessmentViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Finish',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.finish.tr(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await _start(viewModel),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
|
|||
|
||||
Widget _buildScaffoldWrapper(CallSupportViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
body: _buildScaffoldContainer(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffoldContainer(CallSupportViewModel viewModel) => Container(
|
||||
decoration: bgDecoration,
|
||||
child: _buildScaffold(viewModel),
|
||||
);
|
||||
Widget _buildScaffold(CallSupportViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyWrapper(viewModel));
|
||||
|
||||
|
|
@ -93,7 +97,7 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
|
|||
const CircularIcon(icon: Icons.call, size: 50, color: kcPrimaryColor);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
LocaleKeys.call_our_support.tr(),
|
||||
LocaleKeys.call_our_support.tr(),
|
||||
style: style25DG600,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -116,7 +120,7 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
|
|||
leadingIcon: Icons.call,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text:LocaleKeys.tap_to_call.tr(),
|
||||
text: LocaleKeys.tap_to_call.tr(),
|
||||
onTap: () async => await viewModel.callSupport(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ class RequestCodeScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
|||
required ForgetPasswordViewModel viewModel}) =>
|
||||
[
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildExpandedBody(context: context, viewModel: viewModel)
|
||||
];
|
||||
|
||||
|
|
@ -96,8 +95,10 @@ class RequestCodeScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _inAppPop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -86,18 +86,17 @@ class ResetPasswordScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
|||
children: _buildScaffoldChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren(ForgetPasswordViewModel viewModel) => [
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildExpandedBody(viewModel)
|
||||
];
|
||||
List<Widget> _buildScaffoldChildren(ForgetPasswordViewModel viewModel) =>
|
||||
[_buildAppBar(viewModel), _buildExpandedBody(viewModel)];
|
||||
|
||||
Widget _buildAppBar(ForgetPasswordViewModel viewModel) => LargeAppBar(
|
||||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _inAppPop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(ForgetPasswordViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -2,16 +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 '../course/course_view.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();
|
||||
|
|
@ -24,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),
|
||||
|
|
@ -78,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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/screens/first_landing_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/screens/fourth_landing_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/screens/second_landing_screen.dart';
|
||||
|
|
@ -23,19 +24,21 @@ class LandingView extends StackedView<LandingViewModel> {
|
|||
LandingViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildLandingScreens(viewModel);
|
||||
_buildLandingScreensWrapper(viewModel);
|
||||
|
||||
Widget _buildLandingScreensWrapper(LandingViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcPrimaryColor,
|
||||
body: _buildLandingScreens(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLandingScreens(LandingViewModel viewModel) => FlutterCarousel(
|
||||
options: FlutterCarouselOptions(
|
||||
autoPlay: true,
|
||||
viewportFraction: 1,
|
||||
showIndicator: true,
|
||||
indicatorMargin: 40,
|
||||
showIndicator: false,
|
||||
height: double.maxFinite,
|
||||
slideIndicator: CircularSlideIndicator(
|
||||
slideIndicatorOptions:
|
||||
const SlideIndicatorOptions(indicatorRadius: 2.5),
|
||||
),
|
||||
controller: viewModel.controller,
|
||||
),
|
||||
items: _buildScreens(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
|
|
@ -11,6 +12,16 @@ class LandingViewModel extends BaseViewModel {
|
|||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
// Controller
|
||||
final FlutterCarouselController _controller = FlutterCarouselController();
|
||||
|
||||
FlutterCarouselController get controller => _controller;
|
||||
|
||||
// In-app navigation
|
||||
void next() {
|
||||
_controller.nextPage();
|
||||
}
|
||||
|
||||
// Navigation
|
||||
Future<void> navigateToLogin() async =>
|
||||
await _navigationService.replaceWithLoginView();
|
||||
|
|
@ -18,6 +29,7 @@ class LandingViewModel extends BaseViewModel {
|
|||
// Remote api call
|
||||
|
||||
// First time install
|
||||
|
||||
Future<void> setFirstTimeInstall() async {
|
||||
await runBusyFuture(_setFirstTimeInstall());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,10 +126,10 @@ class FirstLandingScreen extends ViewModelWidget<LandingViewModel> {
|
|||
Widget _buildContinueButton(LandingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Next',
|
||||
borderRadius: 25,
|
||||
text: 'Get Started',
|
||||
onTap: viewModel.next,
|
||||
backgroundColor: kcWhite,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
onTap: () async => await viewModel.setFirstTimeInstall(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,10 +127,10 @@ class SecondLandingScreen extends ViewModelWidget<LandingViewModel> {
|
|||
Widget _buildContinueButton(LandingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Next',
|
||||
borderRadius: 25,
|
||||
text: 'Get Started',
|
||||
onTap: viewModel.next,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await viewModel.setFirstTimeInstall(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,15 +122,15 @@ class ThirdLandingScreen extends ViewModelWidget<LandingViewModel> {
|
|||
viewModel.isBusy ? _buildIndicator() : _buildContinueButton(viewModel);
|
||||
|
||||
Widget _buildIndicator() =>
|
||||
const CustomCircularProgressIndicator(color: kcWhite);
|
||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||
|
||||
Widget _buildContinueButton(LandingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Next',
|
||||
borderRadius: 25,
|
||||
text: 'Get Started',
|
||||
onTap: viewModel.next,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await viewModel.setFirstTimeInstall(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,16 +108,16 @@ class LanguageView extends StackedView<LanguageViewModel> {
|
|||
Widget _buildAppbar(LanguageViewModel viewModel) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onPop: viewModel.pop,
|
||||
title:LocaleKeys.language_preference.tr() ,
|
||||
title: LocaleKeys.language_preference.tr(),
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
LocaleKeys.choose_your_language.tr(),
|
||||
LocaleKeys.choose_your_language.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.switch_language_anytime.tr() ,
|
||||
LocaleKeys.switch_language_anytime.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,12 @@ class LanguageViewModel extends ReactiveViewModel {
|
|||
|
||||
Future<void> setSelectedLanguage(
|
||||
{required BuildContext context,
|
||||
required Map<String, dynamic> title}) async =>
|
||||
await _localizationService.setSelectedLanguage(
|
||||
context: context, title: title);
|
||||
required Map<String, dynamic> title}) async {
|
||||
await _localizationService.setSelectedLanguage(
|
||||
context: context, title: title);
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
|
|
|||
|
|
@ -6,14 +6,48 @@ import '../../common/app_colors.dart';
|
|||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/finish_practice_sheet.dart';
|
||||
import '../../widgets/learn_course_tile.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_course_viewmodel.dart';
|
||||
|
||||
class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
||||
final int id;
|
||||
|
||||
const LearnCourseView({Key? key, required this.id}) : super(key: key);
|
||||
|
||||
Future<void> _onPractice(
|
||||
{required BuildContext context,
|
||||
required LearnCourse course,
|
||||
required LearnCourseViewModel viewModel}) async {
|
||||
if (course.access?.completedCount == course.access?.totalCount) {
|
||||
await viewModel.navigateToLearnPractice(
|
||||
id: course.id ?? 0, level: course.name ?? '');
|
||||
} else {
|
||||
if (course.access?.isAccessible ?? false) {
|
||||
await _showSheet(
|
||||
context: context,
|
||||
viewModel: viewModel,
|
||||
practice: PracticeReason.course);
|
||||
} else {
|
||||
await _showSheet(
|
||||
context: context,
|
||||
viewModel: viewModel,
|
||||
practice: PracticeReason.previousCourse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required PracticeReason practice,
|
||||
required LearnCourseViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (_) => _buildSheet(viewModel: viewModel, practice: practice),
|
||||
);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnCourseViewModel viewModel) async {
|
||||
await viewModel.getLearnCourses(id);
|
||||
|
|
@ -30,32 +64,46 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
|||
LearnCourseViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnCourseViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffoldContainer(viewModel),
|
||||
body: _buildScaffoldContainer(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffoldContainer(LearnCourseViewModel viewModel) => Container(
|
||||
Widget _buildScaffoldContainer(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
Container(
|
||||
decoration: bgDecoration,
|
||||
child: _buildScaffold(viewModel),
|
||||
child: _buildScaffold(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnCourseViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
Widget _buildScaffold(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
SafeArea(child: _buildBody(context: context, viewModel: viewModel));
|
||||
|
||||
Widget _buildBody(LearnCourseViewModel viewModel) => Padding(
|
||||
Widget _buildBody(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
child: _buildColumn(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnCourseViewModel viewModel) => Column(
|
||||
Widget _buildColumn(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildCoursesColumnWrapper(viewModel)
|
||||
_buildCoursesColumnWrapper(context: context, viewModel: viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -64,34 +112,46 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
|||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildCoursesColumnWrapper(LearnCourseViewModel viewModel) =>
|
||||
Expanded(child: _buildLevelsColumnScrollView(viewModel));
|
||||
Widget _buildCoursesColumnWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
Expanded(
|
||||
child: _buildLevelsColumnScrollView(
|
||||
context: context, viewModel: viewModel));
|
||||
|
||||
Widget _buildLevelsColumnScrollView(LearnCourseViewModel viewModel) =>
|
||||
Widget _buildLevelsColumnScrollView(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildListViewBuilder(viewModel),
|
||||
child: _buildListViewBuilder(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnCourseViewModel viewModel) =>
|
||||
Widget _buildListViewBuilder(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
viewModel.busy(StateObjects.learnCourses)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
: _buildListView(context: context, viewModel: viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnCourseViewModel viewModel) => ListView.separated(
|
||||
Widget _buildListView(
|
||||
{required BuildContext context,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.courses.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
course: viewModel.courses[index],
|
||||
onPracticeTap: () async => await _onPractice(
|
||||
context: context,
|
||||
viewModel: viewModel,
|
||||
course: viewModel.courses[index]),
|
||||
onViewTap: () async =>
|
||||
await viewModel.navigateToLearnModule(viewModel.courses[index]),
|
||||
onPracticeTap: () async => await viewModel.navigateToLearnPractice(
|
||||
id: viewModel.courses[index].id ?? 0,
|
||||
level: viewModel.courses[index].name ?? ''),
|
||||
),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
);
|
||||
|
|
@ -106,4 +166,12 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
|||
onViewTap: onViewTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
|
||||
Widget _buildSheet(
|
||||
{required PracticeReason practice,
|
||||
required LearnCourseViewModel viewModel}) =>
|
||||
FinishPracticeSheet(
|
||||
practice: practice,
|
||||
onTap: viewModel.pop,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ class LearnCourseViewModel extends ReactiveViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnSubscription() async =>
|
||||
await _navigationService.navigateToLearnSubscriptionView();
|
||||
|
||||
Future<void> navigateToLearnModule(LearnCourse course) async =>
|
||||
_navigationService.navigateToLearnModuleView(course: course);
|
||||
|
||||
|
|
@ -39,7 +42,7 @@ class LearnCourseViewModel extends ReactiveViewModel {
|
|||
level: level,
|
||||
practice: LearnPractices.course,
|
||||
label: LocaleKeys.begin_level_practice.tr(),
|
||||
subtitle: LocaleKeys.lets_quick_practice.tr(),
|
||||
subtitle: LocaleKeys.lets_quick_review.tr(),
|
||||
title: '${LocaleKeys.lets_practice_course.tr()} $level',
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import 'package:yimaru_app/ui/widgets/motivation_card.dart';
|
|||
import '../../common/app_colors.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/finish_practice_sheet.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_lesson_viewmodel.dart';
|
||||
|
||||
|
|
@ -20,6 +21,44 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
|
||||
const LearnLessonView({Key? key, required this.module}) : super(key: key);
|
||||
|
||||
Future<void> _onPractice(
|
||||
{required int index,
|
||||
required LearnLesson lesson,
|
||||
required BuildContext context,
|
||||
required LearnLessonViewModel viewModel}) async {
|
||||
/* if (lesson.access?.isAccessible ?? false) {
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
} else {
|
||||
await _showSheet(context: context, viewModel: viewModel);
|
||||
}*/
|
||||
if (index > 1) {
|
||||
if (viewModel.user?.subscriptionStatus?.toLowerCase() == 'subscribed') {
|
||||
if (lesson.access?.isAccessible ?? false) {
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
} else {
|
||||
await _showSheet(context: context, viewModel: viewModel);
|
||||
}
|
||||
} else {
|
||||
await viewModel.navigateToLearnSubscription();
|
||||
}
|
||||
} else {
|
||||
if (lesson.access?.isAccessible ?? false) {
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
} else {
|
||||
await _showSheet(context: context, viewModel: viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required LearnLessonViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (_) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnLessonViewModel viewModel) async {
|
||||
await viewModel.getLessons(module.id ?? 0);
|
||||
|
|
@ -124,13 +163,13 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
verticalSpaceTiny,
|
||||
_buildSubtitle(),
|
||||
verticalSpaceSmall,
|
||||
_buildModuleProgress(),
|
||||
_buildModuleProgress(viewModel),
|
||||
verticalSpaceLarge,
|
||||
_buildMotivationCard(),
|
||||
verticalSpaceLarge,
|
||||
_buildHeader(),
|
||||
verticalSpaceMedium,
|
||||
_buildListViewBuilder(viewModel),
|
||||
_buildListViewBuilder(context: context, viewModel: viewModel),
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
|
|
@ -143,55 +182,82 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
style: style14DG500,
|
||||
);
|
||||
|
||||
Widget _buildModuleProgress() => ModuleProgress(
|
||||
total: module.access?.totalCount ?? 0,
|
||||
completed: module.access?.completedCount ?? 0,
|
||||
progress: module.access?.progressPercent ?? 0,
|
||||
Widget _buildModuleProgress(LearnLessonViewModel viewModel) => ModuleProgress(
|
||||
total: (viewModel.getUpdatedLearnModule(module.id ?? 0) ?? module)
|
||||
.access
|
||||
?.totalCount ??
|
||||
0,
|
||||
completed: (viewModel.getUpdatedLearnModule(module.id ?? 0) ?? module)
|
||||
.access
|
||||
?.completedCount ??
|
||||
0,
|
||||
progress: (viewModel.getUpdatedLearnModule(module.id ?? 0) ?? module)
|
||||
.access
|
||||
?.progressPercent ??
|
||||
0,
|
||||
);
|
||||
|
||||
Widget _buildMotivationCard() => const MotivationCard();
|
||||
|
||||
Widget _buildHeader() => Text(
|
||||
LocaleKeys.lessons_in_module.tr(),
|
||||
LocaleKeys.lessons_in_module.tr(),
|
||||
style: style18DG700,
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnLessonViewModel viewModel) =>
|
||||
Widget _buildListViewBuilder(
|
||||
{required BuildContext context,
|
||||
required LearnLessonViewModel viewModel}) =>
|
||||
viewModel.busy(StateObjects.learnLessons)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
: _buildListView(context: context, viewModel: viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnLessonViewModel viewModel) => ListView.builder(
|
||||
Widget _buildListView(
|
||||
{required BuildContext context,
|
||||
required LearnLessonViewModel viewModel}) =>
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.lessons.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
index: index,
|
||||
lesson: viewModel.lessons[index],
|
||||
last: index == viewModel.lessons.length - 1,
|
||||
onPracticeTap: () async => await _onPractice(
|
||||
index: index,
|
||||
context: context,
|
||||
viewModel: viewModel,
|
||||
lesson: viewModel.lessons[index],
|
||||
),
|
||||
onLessonTap: () async => await viewModel.navigateToLearnLessonDetail(
|
||||
index: index,
|
||||
module: module,
|
||||
lesson: viewModel.lessons[index],
|
||||
hasPractice:
|
||||
index != viewModel.lessons.length - 1 ? true : false),
|
||||
onPracticeTap: () async => await viewModel
|
||||
.navigateToLearnPractice(viewModel.lessons[index].id ?? 0),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required bool last,
|
||||
required int index,
|
||||
required LearnLesson lesson,
|
||||
required GestureTapCallback? onLessonTap,
|
||||
required GestureTapCallback? onPracticeTap,
|
||||
}) =>
|
||||
LearnLessonTile(
|
||||
last: last,
|
||||
index: index,
|
||||
lesson: lesson,
|
||||
onLessonTap: onLessonTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnLessonViewModel viewModel) => FinishPracticeSheet(
|
||||
onTap: viewModel.pop,
|
||||
practice: PracticeReason.lesson,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
|||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/learn_module.dart';
|
||||
import '../../../models/user.dart';
|
||||
import '../../../services/authentication_service.dart';
|
||||
import '../../../services/learn_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/helper_functions.dart';
|
||||
|
||||
class LearnLessonViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
|
|
@ -19,10 +22,23 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
|||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_learnService, _authenticationService];
|
||||
|
||||
// Current user
|
||||
User? get _user => _authenticationService.user;
|
||||
|
||||
User? get user => _user;
|
||||
|
||||
// Learn lessons
|
||||
|
||||
final Map<int, String> _refreshedThumbnails = {};
|
||||
|
||||
Map<int, String> get refreshedThumbnails => _refreshedThumbnails;
|
||||
|
||||
List<LearnLesson> get _lessons => _learnService.lessons;
|
||||
|
||||
List<LearnLesson> get lessons => _lessons;
|
||||
|
|
@ -34,17 +50,24 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
|||
await _navigationService.navigateToLearnPracticeView(
|
||||
id: id,
|
||||
practice: LearnPractices.lesson,
|
||||
label:LocaleKeys.start_practice.tr(),
|
||||
label: LocaleKeys.start_practice.tr(),
|
||||
title: LocaleKeys.lets_practice_module.tr(),
|
||||
subtitle:LocaleKeys.ask_you_few_actions.tr(),
|
||||
subtitle: LocaleKeys.ask_you_few_actions.tr(),
|
||||
);
|
||||
|
||||
Future<void> navigateToLearnSubscription() async =>
|
||||
await _navigationService.navigateToLearnSubscriptionView();
|
||||
|
||||
Future<void> navigateToLearnLessonDetail(
|
||||
{required bool hasPractice,
|
||||
{required int index,
|
||||
required bool hasPractice,
|
||||
required LearnLesson lesson,
|
||||
required LearnModule module}) async =>
|
||||
await _navigationService.navigateToLearnLessonDetailView(
|
||||
lesson: lesson, module: module, hasPractice: hasPractice);
|
||||
index: index,
|
||||
lesson: lesson,
|
||||
module: module,
|
||||
hasPractice: hasPractice);
|
||||
|
||||
// Remote api call
|
||||
|
||||
|
|
@ -55,6 +78,32 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
|||
Future<void> _getLessons(int id) async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _learnService.getLearnLessons(id);
|
||||
// await refreshLessonImages(_lessons);
|
||||
}
|
||||
}
|
||||
|
||||
// Get module
|
||||
LearnModule? getUpdatedLearnModule(int id) {
|
||||
return _learnService.getLearnModuleById(id);
|
||||
}
|
||||
|
||||
//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] ?? '') ?? '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,20 +15,37 @@ import '../../widgets/small_app_bar.dart';
|
|||
import 'learn_lesson_detail_viewmodel.dart';
|
||||
|
||||
class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||
final int index;
|
||||
final bool hasPractice;
|
||||
final LearnModule module;
|
||||
final LearnLesson lesson;
|
||||
|
||||
const LearnLessonDetailView(
|
||||
{Key? key,
|
||||
required this.index,
|
||||
required this.lesson,
|
||||
required this.module,
|
||||
required this.hasPractice})
|
||||
: super(key: key);
|
||||
|
||||
Future<void> _navigate(LearnLessonDetailViewModel viewModel) async {
|
||||
await viewModel.pause();
|
||||
Future<void> _onPractice(
|
||||
{required LearnLesson lesson,
|
||||
required LearnLessonDetailViewModel viewModel}) async {
|
||||
/* await viewModel.pause();
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
*/
|
||||
if (index > 1) {
|
||||
if (viewModel.user?.subscriptionStatus?.toLowerCase() == 'subscribed') {
|
||||
await viewModel.pause();
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
} else {
|
||||
await viewModel.pause();
|
||||
await viewModel.navigateToLearnSubscription();
|
||||
}
|
||||
} else {
|
||||
await viewModel.pause();
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -192,7 +209,8 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text:LocaleKeys.take_practice.tr() ,
|
||||
onTap: () async => await _navigate(viewModel),
|
||||
text: LocaleKeys.take_practice.tr(),
|
||||
onTap: () async =>
|
||||
await _onPractice(lesson: lesson, viewModel: viewModel),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ import 'package:yimaru_app/ui/common/enmus.dart';
|
|||
import '../../../app/app.locator.dart';
|
||||
import '../../../app/app.router.dart';
|
||||
import '../../../models/learn_lesson.dart';
|
||||
import '../../../models/user.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/authentication_service.dart';
|
||||
import '../../../services/learn_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../../services/vimeo_service.dart';
|
||||
|
|
@ -26,8 +28,16 @@ class LearnLessonDetailViewModel extends ReactiveViewModel {
|
|||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_learnService, _authenticationService];
|
||||
|
||||
// Current user
|
||||
User? get _user => _authenticationService.user;
|
||||
|
||||
User? get user => _user;
|
||||
|
||||
// Learn lessons
|
||||
List<LearnLesson> get _lessons => _learnService.lessons;
|
||||
|
|
@ -121,6 +131,9 @@ class LearnLessonDetailViewModel extends ReactiveViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnSubscription() async =>
|
||||
await _navigationService.navigateToLearnSubscriptionView();
|
||||
|
||||
Future<void> navigateToLearnPractice(int id) async =>
|
||||
await _navigationService.navigateToLearnPracticeView(
|
||||
id: id,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import '../../common/app_colors.dart';
|
|||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/finish_practice_sheet.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_module_viewmodel.dart';
|
||||
|
||||
|
|
@ -19,6 +20,41 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
|
||||
const LearnModuleView({Key? key, required this.course}) : super(key: key);
|
||||
|
||||
Future<void> _onPractice(
|
||||
{required BuildContext context,
|
||||
required LearnModule module,
|
||||
required LearnModuleViewModel viewModel}) async {
|
||||
if (module.access?.completedCount == module.access?.totalCount) {
|
||||
await viewModel.navigateToLearnPractice(
|
||||
id: module.id ?? 0, module: module.name ?? '');
|
||||
} else {
|
||||
if (module.access?.isAccessible ?? false) {
|
||||
print('Accessible');
|
||||
await _showSheet(
|
||||
context: context,
|
||||
viewModel: viewModel,
|
||||
practice: PracticeReason.module);
|
||||
} else {
|
||||
print('Inaccessible');
|
||||
|
||||
await _showSheet(
|
||||
context: context,
|
||||
viewModel: viewModel,
|
||||
practice: PracticeReason.previousModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required PracticeReason practice,
|
||||
required LearnModuleViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (_) => _buildSheet(viewModel: viewModel, practice: practice),
|
||||
);
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnModuleViewModel viewModel) async {
|
||||
await viewModel.getLearnModules(course.id ?? 0);
|
||||
|
|
@ -35,32 +71,46 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
LearnModuleViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnModuleViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffoldContainer(viewModel),
|
||||
body: _buildScaffoldContainer(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffoldContainer(LearnModuleViewModel viewModel) => Container(
|
||||
Widget _buildScaffoldContainer(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Container(
|
||||
decoration: bgDecoration,
|
||||
child: _buildScaffold(viewModel),
|
||||
child: _buildScaffold(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnModuleViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
Widget _buildScaffold(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
SafeArea(child: _buildBody(context: context, viewModel: viewModel));
|
||||
|
||||
Widget _buildBody(LearnModuleViewModel viewModel) => Padding(
|
||||
Widget _buildBody(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
child: _buildColumn(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnModuleViewModel viewModel) => Column(
|
||||
Widget _buildColumn(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildModulesColumnWrapper(viewModel),
|
||||
_buildModulesColumnWrapper(context: context, viewModel: viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -69,28 +119,41 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildModulesColumnWrapper(LearnModuleViewModel viewModel) =>
|
||||
Expanded(child: _buildLevelsColumnScrollView(viewModel));
|
||||
Widget _buildModulesColumnWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Expanded(
|
||||
child: _buildLevelsColumnScrollView(
|
||||
context: context, viewModel: viewModel));
|
||||
|
||||
Widget _buildLevelsColumnScrollView(LearnModuleViewModel viewModel) =>
|
||||
Widget _buildLevelsColumnScrollView(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildLevelsColumn(viewModel),
|
||||
child: _buildLevelsColumn(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLevelsColumn(LearnModuleViewModel viewModel) => Column(
|
||||
Widget _buildLevelsColumn(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: _buildLevelsColumnChildren(viewModel),
|
||||
children:
|
||||
_buildLevelsColumnChildren(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildLevelsColumnChildren(LearnModuleViewModel viewModel) => [
|
||||
List<Widget> _buildLevelsColumnChildren(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
[
|
||||
verticalSpaceSmall,
|
||||
_buildTitle(),
|
||||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildOverallProgress(),
|
||||
_buildOverallProgress(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildListViewBuilder(viewModel)
|
||||
_buildListViewBuilder(context: context, viewModel: viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
|
|
@ -99,34 +162,45 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.your_current_level.tr(),
|
||||
LocaleKeys.your_current_level.tr(),
|
||||
style: style14P400,
|
||||
);
|
||||
|
||||
Widget _buildOverallProgress() => OverallProgress(
|
||||
Widget _buildOverallProgress(LearnModuleViewModel viewModel) =>
|
||||
OverallProgress(
|
||||
indicatorBackgroundColor: kcWhite,
|
||||
progress: course.access?.progressPercent ?? 0,
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
progress: (viewModel.getUpdatedLearnCourse(course.id ?? 0) ?? course)
|
||||
.access
|
||||
?.progressPercent ??
|
||||
0,
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnModuleViewModel viewModel) =>
|
||||
Widget _buildListViewBuilder(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
viewModel.busy(StateObjects.learnModules)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
: _buildListView(context: context, viewModel: viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnModuleViewModel viewModel) => ListView.builder(
|
||||
Widget _buildListView(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.modules.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
module: viewModel.modules[index],
|
||||
onPracticeTap: () async => await viewModel.navigateToLearnPractice(
|
||||
id: viewModel.modules[index].id ?? 0,
|
||||
module: viewModel.modules[index].name ?? ''),
|
||||
onPracticeTap: () async => await _onPractice(
|
||||
context: context,
|
||||
viewModel: viewModel,
|
||||
module: viewModel.modules[index],
|
||||
),
|
||||
onModuleTap: () async =>
|
||||
await viewModel.navigateToLearnLesson(viewModel.modules[index]),
|
||||
),
|
||||
|
|
@ -141,4 +215,12 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
module: module,
|
||||
onModuleTap: onModuleTap,
|
||||
onPracticeTap: onPracticeTap);
|
||||
|
||||
Widget _buildSheet(
|
||||
{required PracticeReason practice,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
FinishPracticeSheet(
|
||||
practice: practice,
|
||||
onTap: viewModel.pop,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ import 'package:yimaru_app/models/learn_module.dart';
|
|||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/learn_course.dart';
|
||||
import '../../../services/learn_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/helper_functions.dart';
|
||||
|
||||
class LearnModuleViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
|
|
@ -22,6 +24,10 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
|||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||
|
||||
// Learn module
|
||||
final Map<int, String> _refreshedIcons = {};
|
||||
|
||||
Map<int, String> get refreshedIcons => _refreshedIcons;
|
||||
|
||||
List<LearnModule> get _modules => _learnService.modules;
|
||||
|
||||
List<LearnModule> get modules => _modules;
|
||||
|
|
@ -29,8 +35,6 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
|
||||
|
||||
Future<void> navigateToLearnLesson(LearnModule module) async =>
|
||||
await _navigationService.navigateToLearnLessonView(module: module);
|
||||
|
||||
|
|
@ -54,6 +58,32 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
|||
Future<void> _getLearnModules(int id) async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _learnService.getLearnModules(id);
|
||||
await refreshModuleImages(_modules);
|
||||
}
|
||||
}
|
||||
|
||||
// Get course
|
||||
LearnCourse? getUpdatedLearnCourse(int id) {
|
||||
return _learnService.getLearnCourseById(id);
|
||||
}
|
||||
|
||||
//Refresh image
|
||||
Future<void> refreshModuleImages(List<LearnModule> modules) async {
|
||||
for (final module in modules) {
|
||||
final icon = module.icon;
|
||||
|
||||
if (module.id == null || icon == null || icon.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String? refreshedUrl = await _learnService.refreshObject(icon);
|
||||
|
||||
if (refreshedUrl != null) {
|
||||
_refreshedIcons[module.id!] = refreshedUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getModuleImage(LearnModule module) =>
|
||||
getReadableUrl(_refreshedIcons[module.id] ?? '') ?? '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import '../../../services/audio_player_service.dart';
|
|||
import '../../../services/learn_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/helper_functions.dart';
|
||||
|
||||
class LearnPracticeViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
|
|
@ -94,10 +95,15 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
Voice? get playing => _playing;
|
||||
|
||||
// Learn practices
|
||||
|
||||
List<LearnPractice> _practices = [];
|
||||
|
||||
List<LearnPractice> get practices => _practices;
|
||||
|
||||
final Map<int, String> _refreshedImages = {};
|
||||
|
||||
Map<int, String> get refreshedImages => _refreshedImages;
|
||||
|
||||
// Practice questions
|
||||
List<LearnQuestion> _questions = [];
|
||||
|
||||
|
|
@ -260,6 +266,10 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
|
||||
// Remote api call
|
||||
|
||||
// Refresh url
|
||||
Future<String?> refreshUrl(String url) async =>
|
||||
await _learnService.refreshObject(url);
|
||||
|
||||
// Learn practice
|
||||
Future<void> getLearnPractices(
|
||||
{required int id, required LearnPractices practice}) async =>
|
||||
|
|
@ -271,14 +281,14 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
if (await _statusChecker.checkConnection()) {
|
||||
if (practice == LearnPractices.course) {
|
||||
_practices = await _apiService.getLearnCoursePractices(id);
|
||||
|
||||
// await refreshPracticeImages(_practices);
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
} else if (practice == LearnPractices.module) {
|
||||
_practices = await _apiService.getLearnModulePractices(id);
|
||||
// await refreshPracticeImages(_practices);
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
} else {
|
||||
_practices = await _apiService.getLearnLessonPractices(id);
|
||||
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -296,6 +306,27 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
Future<void> _completeLearnPractices() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _apiService.completeLearnPractice(_practices.first.id ?? 0);
|
||||
await _learnService.getLearnProgress();
|
||||
}
|
||||
}
|
||||
|
||||
//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] ?? '') ?? '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ class InteractLearnPracticeScreen
|
|||
showBackButton: true,
|
||||
onPop: () async =>
|
||||
await _showSheet(context: context, viewModel: viewModel),
|
||||
title: '${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})');
|
||||
title:
|
||||
'${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})');
|
||||
|
||||
Widget _buildSpeakingIndicatorWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Column(
|
||||
|
|
@ -154,13 +155,13 @@ class InteractLearnPracticeScreen
|
|||
: const SizedBox(height: 20);
|
||||
|
||||
Widget _buildListeningLabel() => Text(
|
||||
'Daniel ${LocaleKeys.speaking.tr()}',
|
||||
'Daniel ${LocaleKeys.speaking.tr()}',
|
||||
style: style14P400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildSpeakingLabel() => Text(
|
||||
'${ LocaleKeys.you_are_speaking.tr()}...',
|
||||
'${LocaleKeys.you_are_speaking.tr()}...',
|
||||
style: style14P400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -239,7 +240,7 @@ class InteractLearnPracticeScreen
|
|||
];
|
||||
|
||||
Widget _buildActionLabel() => Text(
|
||||
LocaleKeys.tap_microphone.tr(),
|
||||
LocaleKeys.tap_microphone.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -359,7 +360,7 @@ class InteractLearnPracticeScreen
|
|||
CustomColumnButton(
|
||||
color: kcRed,
|
||||
icon: Icons.close,
|
||||
label:LocaleKeys.cancel.tr() ,
|
||||
label: LocaleKeys.cancel.tr(),
|
||||
onTap: () async =>
|
||||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class LearnLoadingScreen extends StatelessWidget {
|
|||
Align(alignment: Alignment.center, child: _buildRefreshButton());
|
||||
|
||||
Widget _buildRefreshButton() => NoDataIndicator(
|
||||
onTap: onTap,
|
||||
title: LocaleKeys.no_practice_available.tr(),
|
||||
onTap: onTap,
|
||||
title: LocaleKeys.no_practice_available.tr(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@ class LearnPracticeAppreciationScreen
|
|||
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
const LearnPracticeAppreciationScreen({super.key});
|
||||
|
||||
Future<void> _reset(LearnPracticeViewModel viewModel) async =>
|
||||
await viewModel.reset();
|
||||
|
||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||
await viewModel.stopRecording();
|
||||
viewModel.pop();
|
||||
|
|
@ -139,7 +136,7 @@ class LearnPracticeAppreciationScreen
|
|||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.you_have_finished_practice.tr(),
|
||||
LocaleKeys.you_have_finished_practice.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -158,6 +155,5 @@ class LearnPracticeAppreciationScreen
|
|||
onTap: () => viewModel.goTo(4),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.view_results.tr(),
|
||||
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,6 +180,6 @@ class LearnPracticeDescriptionScreen
|
|||
foregroundColor: kcWhite,
|
||||
onTap: () => viewModel.goTo(2),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.start_practice.tr() ,
|
||||
text: LocaleKeys.start_practice.tr(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class LearnPracticeFinishScreen
|
|||
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
title:LocaleKeys.practice_speaking.tr(),
|
||||
title: LocaleKeys.practice_speaking.tr(),
|
||||
);
|
||||
|
||||
Widget _buildSpeakingIndicatorWrapper(LearnPracticeViewModel viewModel) =>
|
||||
|
|
@ -81,13 +81,13 @@ class LearnPracticeFinishScreen
|
|||
Widget _buildIcon() => SvgPicture.asset('assets/icons/success.svg');
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
LocaleKeys.practice_completed.tr(),
|
||||
LocaleKeys.practice_completed.tr(),
|
||||
style: style25DG600,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.sound_confident.tr() ,
|
||||
LocaleKeys.sound_confident.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -120,7 +120,7 @@ class LearnPracticeFinishScreen
|
|||
onTap: viewModel.pop,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.continue_practice.tr() ,
|
||||
text: LocaleKeys.continue_practice.tr(),
|
||||
);
|
||||
|
||||
Widget _buildPracticeAgainButton(LearnPracticeViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ class LearnPracticeResultScreen
|
|||
required LearnPracticeViewModel viewModel}) =>
|
||||
SmallAppBar(
|
||||
showBackButton: true,
|
||||
title:LocaleKeys.result.tr(),
|
||||
title: LocaleKeys.result.tr(),
|
||||
onPop: () async =>
|
||||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
|
@ -176,7 +176,7 @@ class LearnPracticeResultScreen
|
|||
height: 55,
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text:LocaleKeys.cont.tr(),
|
||||
text: LocaleKeys.cont.tr(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await _navigate(viewModel),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
];
|
||||
|
||||
Widget _buildActionLabel() => Text(
|
||||
LocaleKeys.tap_start_to_listen.tr() ,
|
||||
LocaleKeys.tap_start_to_listen.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -215,8 +215,8 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
|
||||
Widget _buildReplyButtonWrapper() => Expanded(child: _buildReplyButton());
|
||||
|
||||
Widget _buildReplyButton() => CustomColumnButton(
|
||||
icon: Icons.replay, label:LocaleKeys.reply.tr() , color: kcLightGrey);
|
||||
Widget _buildReplyButton() => CustomColumnButton(
|
||||
icon: Icons.replay, label: LocaleKeys.reply.tr(), color: kcLightGrey);
|
||||
|
||||
Widget _buildMicButtonWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildMicButton(viewModel));
|
||||
|
|
|
|||
|
|
@ -89,14 +89,12 @@ class LearnProgramView extends StackedView<LearnProgramViewModel> {
|
|||
program: viewModel.learnPrograms[index],
|
||||
onTap: () async => await viewModel
|
||||
.navigateToLearnCourse(viewModel.learnPrograms[index].id ?? 0),
|
||||
onLockTap: () async => await viewModel.navigateToLearnSubscription(),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required LearnProgram program,
|
||||
required GestureTapCallback onTap,
|
||||
required GestureTapCallback onLockTap,
|
||||
}) =>
|
||||
LearnProgramTile(onTap: onTap, program: program,onLockTap: onLockTap,);
|
||||
LearnProgramTile(onTap: onTap, program: program);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,6 @@ class LearnProgramViewModel extends ReactiveViewModel {
|
|||
Future<void> navigateToLearnCourse(int id) async =>
|
||||
_navigationService.navigateToLearnCourseView(id: id);
|
||||
|
||||
Future<void> navigateToLearnSubscription() async =>
|
||||
await _navigationService.navigateToLearnSubscriptionView();
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn programs
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ class LearnSubscriptionViewModel extends FormViewModel {
|
|||
|
||||
int get selectedIndex => _selectedIndex;
|
||||
|
||||
LearnSubscription? _selectedSubscription;
|
||||
|
||||
LearnSubscription? get selectedSubscription => _selectedSubscription;
|
||||
|
||||
List<LearnSubscription> _subscriptions = [];
|
||||
|
||||
List<LearnSubscription> get subscriptions => _subscriptions;
|
||||
|
|
@ -40,6 +44,13 @@ class LearnSubscriptionViewModel extends FormViewModel {
|
|||
_focusPhoneNumber = true;
|
||||
rebuildUi();
|
||||
}
|
||||
//Learn subscriptions
|
||||
|
||||
void setSelectedPricing(int index) {
|
||||
_selectedIndex = index;
|
||||
_selectedSubscription = _subscriptions[index];
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// In-app navigation
|
||||
|
||||
|
|
@ -60,16 +71,11 @@ class LearnSubscriptionViewModel extends FormViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToArifPay(String phone) async {
|
||||
Future<void> navigateToArifPay(
|
||||
{required String phone, required LearnSubscription? subscription}) async {
|
||||
pop();
|
||||
await _navigationService.navigateToArifPayView(phone: phone);
|
||||
}
|
||||
|
||||
//Learn subscriptions
|
||||
|
||||
void setSelectedPricing(int index) {
|
||||
_selectedIndex = index;
|
||||
rebuildUi();
|
||||
await _navigationService.navigateToPaymentView(
|
||||
phone: phone, subscription: subscription ?? _subscriptions[0]);
|
||||
}
|
||||
|
||||
// Remote api call
|
||||
|
|
@ -82,7 +88,6 @@ class LearnSubscriptionViewModel extends FormViewModel {
|
|||
Future<void> _getLearnSubscriptions() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_subscriptions = await _apiService.getLearnSubscriptions();
|
||||
_subscriptions = _subscriptions + _subscriptions + _subscriptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user