Compare commits

..

No commits in common. "b97e672dfed55497c1d5cb9651ff6575a6f149fe" and "2d5039486f28870ce1ade73f93ab94af9802dfe6" have entirely different histories.

21 changed files with 130 additions and 140 deletions

View File

@ -1,4 +1,5 @@
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:yimaru_app/models/option.dart';
part 'learn_question.g.dart'; part 'learn_question.g.dart';

View File

@ -14,7 +14,7 @@ class VoiceRecorderService with ListenableServiceMixin {
WaveformRecorderController get waveController => _waveController; WaveformRecorderController get waveController => _waveController;
final bool _isRecording = false; bool _isRecording = false;
bool get isRecording => _isRecording; bool get isRecording => _isRecording;

View File

@ -29,7 +29,7 @@ enum DuolingoAssessments { speaking, reading, writing, listening }
enum StateObjects { enum StateObjects {
none, none,
courses, courses,
startupView, homeView,
register, register,
verifyOtp, verifyOtp,
resendOtp, resendOtp,

View File

@ -240,8 +240,8 @@ class AssessmentViewModel extends BaseViewModel {
// Navigation // Navigation
void pop() => _navigationService.back(); void pop() => _navigationService.back();
Future<void> replaceWithStartUp() async => Future<void> replaceWithHome() async =>
await _navigationService.clearStackAndShow(Routes.startupView); await _navigationService.clearStackAndShow(Routes.homeView);
Future<void> navigateToLanguage() async => Future<void> navigateToLanguage() async =>
await _navigationService.navigateToLanguageView(); await _navigationService.navigateToLanguageView();
@ -259,7 +259,7 @@ class AssessmentViewModel extends BaseViewModel {
await _apiService.completeProfile(_userData); await _apiService.completeProfile(_userData);
if (response['status'] == ResponseStatus.success) { if (response['status'] == ResponseStatus.success) {
clearUserData(); clearUserData();
await replaceWithStartUp(); await replaceWithHome();
showSuccessToast(response['message']); showSuccessToast(response['message']);
} else { } else {
showErrorToast(response['message']); showErrorToast(response['message']);

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/app_colors.dart';
import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/common/enmus.dart';
import 'package:yimaru_app/ui/views/course_category/course_category_view.dart';
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'; import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
import 'package:yimaru_app/ui/views/profile/profile_view.dart'; import 'package:yimaru_app/ui/views/profile/profile_view.dart';
import 'package:yimaru_app/ui/views/startup/startup_view.dart'; import 'package:yimaru_app/ui/views/startup/startup_view.dart';
@ -15,12 +16,27 @@ class HomeView extends StackedView<HomeViewModel> {
@override @override
HomeViewModel viewModelBuilder(BuildContext context) => HomeViewModel(); HomeViewModel viewModelBuilder(BuildContext context) => HomeViewModel();
@override
void onViewModelReady(HomeViewModel viewModel) async {
// Removable
await _init(viewModel);
super.onViewModelReady(viewModel);
}
Future<void> _init(HomeViewModel viewModel) async =>
await viewModel.initialize();
@override @override
Widget builder( Widget builder(
BuildContext context, HomeViewModel viewModel, Widget? child) => BuildContext context, HomeViewModel viewModel, Widget? child) =>
_buildScaffold(viewModel); _buildScaffoldWrapper(viewModel);
Widget _buildScaffoldWrapper(HomeViewModel viewModel) =>
viewModel.busy(StateObjects.homeView)
? _buildStartUpView()
: _buildScaffold(viewModel);
Widget _buildStartUpView() => const StartupView(label: 'Checking user info');
Widget _buildScaffold(HomeViewModel viewModel) => Scaffold( Widget _buildScaffold(HomeViewModel viewModel) => Scaffold(
body: getViewForIndex(viewModel.currentPage), body: getViewForIndex(viewModel.currentPage),

View File

@ -14,9 +14,12 @@ import '../../common/enmus.dart';
import '../../common/ui_helpers.dart'; import '../../common/ui_helpers.dart';
class HomeViewModel extends ReactiveViewModel { class HomeViewModel extends ReactiveViewModel {
final _apiService = locator<ApiService>();
final _statusChecker = locator<StatusCheckerService>();
final _navigationService = locator<NavigationService>();
final _bottomSheetService = locator<BottomSheetService>(); final _bottomSheetService = locator<BottomSheetService>();
final _authenticationService = locator<AuthenticationService>(); final _authenticationService = locator<AuthenticationService>();
final _imageDownloaderService = locator<ImageDownloaderService>();
@override @override
List<ListenableServiceMixin> get listenableServices => List<ListenableServiceMixin> get listenableServices =>
@ -46,9 +49,75 @@ class HomeViewModel extends ReactiveViewModel {
rebuildUi(); rebuildUi();
} }
// Save profile status
Future<void> saveProfileStatus(bool value) async =>
await _authenticationService.saveProfileStatus(value);
// Navigation
Future<void> replaceWithFailure() async => await _navigationService
.replaceWithFailureView(label: 'Check you internet connection');
Future<void> replaceWithOnboarding() async =>
await _navigationService.replaceWithOnboardingView();
// Remote api calls
// Initialize user data
Future<void> initialize() async =>
await runBusyFuture(_initialize(), busyObject: StateObjects.homeView);
Future<void> _initialize() async {
await _getProfileStatus();
await _getProfileData();
}
// Get profile data
Future<void> _getProfileData() async {
if (!(_user?.userInfoLoaded ?? false)) {
Map<String, dynamic> response = {};
if (_user?.profileCompleted != null &&
(_user?.profileCompleted ?? false)) {
if (await _statusChecker.checkConnection()) {
response = await _apiService.getProfileData(_user?.userId);
if (response['status'] == ResponseStatus.success) {
User user = response['data'] as User;
await _authenticationService.saveUserData(user);
String image =
await _imageDownloaderService.downloader(user.profilePicture);
await _authenticationService.saveProfilePicture(image);
}
} else {
await replaceWithFailure();
}
}
}
}
// Get profile status
Future<void> _getProfileStatus() async {
Map<String, dynamic> response = {};
if (_user?.profileCompleted == null) {
if (await _statusChecker.checkConnection()) {
response = await _apiService.getProfileStatus(_user);
} else {
await Future.delayed(kDuration);
await replaceWithFailure();
}
} else if (!(_user?.profileCompleted ?? false)) {
response = {'data': false, 'status': ResponseStatus.success};
} else {
response = {'data': true, 'status': ResponseStatus.success};
}
if (response['status'] == ResponseStatus.success && !response['data']) {
await replaceWithOnboarding();
} else if (response['status'] == ResponseStatus.success &&
response['data']) {
await saveProfileStatus(response['data']);
}
}
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
import 'package:yimaru_app/app/app.router.dart';
import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/common/enmus.dart';
import '../../../app/app.locator.dart'; import '../../../app/app.locator.dart';

View File

@ -5,7 +5,9 @@ import 'package:yimaru_app/ui/views/learn_practice/screens/finish_learn_practice
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_completion_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_completion_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_result_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_result_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_questions_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_questions_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/interact_learn_practice_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_intro_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_intro_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/start_learn_practice_screen.dart';
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart'; import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
import '../../common/app_colors.dart'; import '../../common/app_colors.dart';

View File

@ -11,6 +11,7 @@ import 'package:yimaru_app/ui/common/enmus.dart';
import '../../../app/app.locator.dart'; import '../../../app/app.locator.dart';
import '../../../models/learn_question.dart'; import '../../../models/learn_question.dart';
import '../../../models/question.dart';
import '../../../services/api_service.dart'; import '../../../services/api_service.dart';
import '../../../services/audio_player_service.dart'; import '../../../services/audio_player_service.dart';
import '../../../services/status_checker_service.dart'; import '../../../services/status_checker_service.dart';
@ -95,7 +96,7 @@ class LearnPracticeViewModel extends ReactiveViewModel {
List<LearnQuestion> get questions => _questions; List<LearnQuestion> get questions => _questions;
// Practice answers // Practice answers
final List<Map<String, dynamic>> _answers = []; List<Map<String, dynamic>> _answers = [];
List<Map<String, dynamic>> get answers => _answers; List<Map<String, dynamic>> get answers => _answers;

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:yimaru_app/models/learn_practice.dart';
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart'; import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
import '../../../common/app_colors.dart'; import '../../../common/app_colors.dart';

View File

@ -1,8 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:yimaru_app/models/learn_question.dart'; import 'package:yimaru_app/models/learn_question.dart';
import 'package:yimaru_app/models/practice.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/start_learn_practice_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/start_learn_practice_screen.dart';
import 'package:yimaru_app/ui/widgets/learn_practice_card.dart';
import '../../../common/app_colors.dart';
import '../../../common/ui_helpers.dart';
import '../../../widgets/small_app_bar.dart';
import '../learn_practice_viewmodel.dart'; import '../learn_practice_viewmodel.dart';
import 'interact_learn_practice_screen.dart'; import 'interact_learn_practice_screen.dart';

View File

@ -154,7 +154,8 @@ class LoginViewModel extends ReactiveViewModel
} }
// Navigation // Navigation
Future<void> replaceWithHome() async =>
await _navigationService.clearStackAndShow(Routes.homeView);
Future<void> navigateToRegister() async => Future<void> navigateToRegister() async =>
await _navigationService.navigateToRegisterView(); await _navigationService.navigateToRegisterView();
@ -162,12 +163,6 @@ class LoginViewModel extends ReactiveViewModel
Future<void> navigateToForgetPassword() async => Future<void> navigateToForgetPassword() async =>
await _navigationService.navigateToForgetPasswordView(); await _navigationService.navigateToForgetPasswordView();
Future<void> replaceWithStartUp() async =>
await _navigationService.clearStackAndShow(Routes.startupView);
// Remote api calls // Remote api calls
// Login with email // Login with email
@ -187,7 +182,7 @@ class LoginViewModel extends ReactiveViewModel
await _authenticationService.saveUserCredential(data); await _authenticationService.saveUserCredential(data);
clearUserData(); clearUserData();
await replaceWithStartUp(); await replaceWithHome();
showSuccessToast(response['message']); showSuccessToast(response['message']);
} else { } else {
showErrorToast(response['message']); showErrorToast(response['message']);
@ -215,7 +210,7 @@ class LoginViewModel extends ReactiveViewModel
}; };
await _authenticationService.saveUserCredential(data); await _authenticationService.saveUserCredential(data);
clearUserData(); clearUserData();
await replaceWithStartUp(); await replaceWithHome();
showSuccessToast(response['message']); showSuccessToast(response['message']);
} else { } else {
showErrorToast(response['message']); showErrorToast(response['message']);
@ -256,7 +251,7 @@ class LoginViewModel extends ReactiveViewModel
}; };
await _authenticationService.saveUserCredential(data); await _authenticationService.saveUserCredential(data);
await replaceWithStartUp(); await replaceWithHome();
showSuccessToast(response['message']); showSuccessToast(response['message']);
} else { } else {
showErrorToast(response['message']); showErrorToast(response['message']);

View File

@ -32,6 +32,7 @@ class OnboardingView extends StackedView<OnboardingViewModel>
void _initUserData(OnboardingViewModel viewModel) { void _initUserData(OnboardingViewModel viewModel) {
fullNameController.text = viewModel.googleUser?.displayName ?? ''; fullNameController.text = viewModel.googleUser?.displayName ?? '';
print('Full-NAME: ${fullNameController.text}');
} }
void _initClearData() { void _initClearData() {

View File

@ -614,5 +614,6 @@ class OnboardingViewModel extends ReactiveViewModel
Future<void> navigateToAssessment() async => Future<void> navigateToAssessment() async =>
await _navigationService.navigateToAssessmentView(data: _userData); await _navigationService.navigateToAssessmentView(data: _userData);
Future<void> replaceWithHome() async =>
await _navigationService.clearStackAndShow(Routes.homeView);
} }

View File

@ -19,6 +19,8 @@ class RegisterViewModel extends ReactiveViewModel
implements FormViewModel { implements FormViewModel {
final _apiService = locator<ApiService>(); final _apiService = locator<ApiService>();
final _smartAuthService = locator<SmartAuthService>();
final _statusChecker = locator<StatusCheckerService>(); final _statusChecker = locator<StatusCheckerService>();
final _navigationService = locator<NavigationService>(); final _navigationService = locator<NavigationService>();
@ -295,8 +297,8 @@ class RegisterViewModel extends ReactiveViewModel
Future<void> replaceToLogin() async => Future<void> replaceToLogin() async =>
await _navigationService.replaceWithLoginView(); await _navigationService.replaceWithLoginView();
Future<void> replaceWithStartUp() async => Future<void> replaceWithHome() async =>
await _navigationService.clearStackAndShow(Routes.startupView); await _navigationService.clearStackAndShow(Routes.homeView);
// Remote api calls // Remote api calls
@ -342,7 +344,7 @@ class RegisterViewModel extends ReactiveViewModel
}; };
await _authenticationService.saveUserCredential(data); await _authenticationService.saveUserCredential(data);
clearUserData(); clearUserData();
await replaceWithStartUp(); await replaceWithHome();
showSuccessToast(response['message']); showSuccessToast(response['message']);
} else { } else {
showErrorToast(response['message']); showErrorToast(response['message']);
@ -365,7 +367,7 @@ class RegisterViewModel extends ReactiveViewModel
}; };
await _authenticationService.saveUserCredential(data); await _authenticationService.saveUserCredential(data);
await replaceWithStartUp(); await replaceWithHome();
showSuccessToast(response['message']); showSuccessToast(response['message']);
} else { } else {
showErrorToast(response['message']); showErrorToast(response['message']);

View File

@ -6,7 +6,6 @@ import 'package:yimaru_app/ui/common/ui_helpers.dart';
import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart'; import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart';
import '../../common/app_colors.dart'; import '../../common/app_colors.dart';
import '../../common/enmus.dart';
import 'startup_viewmodel.dart'; import 'startup_viewmodel.dart';
class StartupView extends StackedView<StartupViewModel> { class StartupView extends StackedView<StartupViewModel> {
@ -19,20 +18,13 @@ class StartupView extends StackedView<StartupViewModel> {
StartupViewModel viewModel, StartupViewModel viewModel,
Widget? child, Widget? child,
) => ) =>
_buildScaffoldWrapper(viewModel); _buildScaffoldWrapper();
Widget _buildScaffoldWrapper(StartupViewModel viewModel) => Scaffold( Widget _buildScaffoldWrapper() => Scaffold(
backgroundColor: kcBackgroundColor, backgroundColor: kcBackgroundColor,
body: _buildScaffoldState(viewModel), body: _buildScaffold(),
); );
Widget _buildScaffoldState(StartupViewModel viewModel) =>
viewModel.busy(StateObjects.startupView)
? _buildStartUpView()
: _buildScaffold();
Widget _buildStartUpView() => const StartupView(label: 'Checking user info');
Widget _buildScaffold() => Stack( Widget _buildScaffold() => Stack(
children: _buildScaffoldChildren(), children: _buildScaffoldChildren(),
); );

View File

@ -5,31 +5,14 @@ import 'package:yimaru_app/services/in_app_update_service.dart';
import '../../../app/app.locator.dart'; import '../../../app/app.locator.dart';
import '../../../app/app.router.dart'; import '../../../app/app.router.dart';
import '../../../models/user.dart';
import '../../../services/api_service.dart';
import '../../../services/image_downloader_service.dart';
import '../../../services/status_checker_service.dart'; import '../../../services/status_checker_service.dart';
import '../../common/enmus.dart';
import '../../common/ui_helpers.dart';
class StartupViewModel extends ReactiveViewModel { class StartupViewModel extends BaseViewModel {
// Dependency injection // Dependency injection
final _apiService = locator<ApiService>();
final _statusChecker = locator<StatusCheckerService>(); final _statusChecker = locator<StatusCheckerService>();
final _navigationService = locator<NavigationService>(); final _navigationService = locator<NavigationService>();
final _inAppUpdateService = locator<InAppUpdateService>(); final _inAppUpdateService = locator<InAppUpdateService>();
final _authenticationService = locator<AuthenticationService>(); final _authenticationService = locator<AuthenticationService>();
final _imageDownloaderService = locator<ImageDownloaderService>();
@override
List<ListenableServiceMixin> get listenableServices =>
[_authenticationService];
// Current user
User? get _user => _authenticationService.user;
User? get user => _user;
// Place anything here that needs to happen before we get into the application // Place anything here that needs to happen before we get into the application
Future runStartupLogic() async { Future runStartupLogic() async {
@ -44,7 +27,7 @@ class StartupViewModel extends ReactiveViewModel {
} else { } else {
if (loggedIn) { if (loggedIn) {
await _authenticationService.getUser(); await _authenticationService.getUser();
await _getProfileStatus(); await _navigationService.replaceWithHomeView();
} else { } else {
// Removable // Removable
await _navigationService.replaceWithLoginView(); await _navigationService.replaceWithLoginView();
@ -52,94 +35,9 @@ class StartupViewModel extends ReactiveViewModel {
} }
} }
// Navigation
Future<void> replaceWithFailure() async => await _navigationService
.replaceWithFailureView(label: 'Check you internet connection');
Future<void> replaceWithOnboarding() async =>
await _navigationService.replaceWithOnboardingView();
Future<void> replaceWithHome() async =>
await _navigationService.replaceWithHomeView();
// Remote api calls
// In-app update
Future<void> _inAppUpdate() async { Future<void> _inAppUpdate() async {
if (await _statusChecker.checkConnection()) { if (await _statusChecker.checkConnection()) {
await _inAppUpdateService.checkForUpdate(); await _inAppUpdateService.checkForUpdate();
} }
} }
// Get profile status
Future<void> _getProfileStatus() async {
Map<String, dynamic> response = {};
if (_user?.profileCompleted == null) {
if (await _statusChecker.checkConnection()) {
print('PATH: 1');
response = await _apiService.getProfileStatus(_user);
} else {
print('PATH: 2');
await Future.delayed(kDuration);
await replaceWithFailure();
}
} else if (!(_user?.profileCompleted ?? false)) {
print('PATH: 3');
response = {'data': false, 'status': ResponseStatus.success};
} else {
print('PATH: 4');
response = {'data': true, 'status': ResponseStatus.success};
}
if (response['status'] == ResponseStatus.success && !response['data']) {
print('PATH: 5');
await replaceWithOnboarding();
} else if (response['status'] == ResponseStatus.success &&
response['data']) {
print('PATH: 6');
await saveProfileStatus(response['data']);
await _getProfileData();
await replaceWithHome();
}
}
// Save profile status
Future<void> saveProfileStatus(bool value) async =>
await _authenticationService.saveProfileStatus(value);
// Get profile data
Future<void> _getProfileData() async {
if (!(_user?.userInfoLoaded ?? false)) {
Map<String, dynamic> response = {};
if (_user?.profileCompleted != null &&
(_user?.profileCompleted ?? false)) {
if (await _statusChecker.checkConnection()) {
response = await _apiService.getProfileData(_user?.userId);
if (response['status'] == ResponseStatus.success) {
User user = response['data'] as User;
await _authenticationService.saveUserData(user);
String image =
await _imageDownloaderService.downloader(user.profilePicture);
await _authenticationService.saveProfilePicture(image);
}
} else {
await replaceWithFailure();
}
}
}
}
} }

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
import 'package:yimaru_app/ui/widgets/speaking_partner_image.dart'; import 'package:yimaru_app/ui/widgets/speaking_partner_image.dart';
import '../common/app_colors.dart'; import '../common/app_colors.dart';

View File

@ -51,7 +51,7 @@ class LearnPracticeCard extends ViewModelWidget<LearnPracticeViewModel> {
); );
Widget _buildStartButton(LearnPracticeViewModel viewModel) => Widget _buildStartButton(LearnPracticeViewModel viewModel) =>
const CustomElevatedButton( CustomElevatedButton(
height: 50, height: 50,
width: 200, width: 200,
borderRadius: 8, borderRadius: 8,

View File

@ -4,6 +4,7 @@ import 'package:yimaru_app/ui/common/app_colors.dart';
import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart';
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart'; import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
import '../../models/learn_practice.dart';
class LearnPracticeTipSection extends ViewModelWidget<LearnPracticeViewModel> { class LearnPracticeTipSection extends ViewModelWidget<LearnPracticeViewModel> {
const LearnPracticeTipSection({super.key}); const LearnPracticeTipSection({super.key});

View File

@ -1,8 +1,9 @@
name: yimaru_app name: yimaru_app
version: 0.1.6+8 version: 0.1.5+7
publish_to: 'none' publish_to: 'none'
description: A new Flutter project. description: A new Flutter project.
environment: environment:
sdk: '>=3.0.3 <4.0.0' sdk: '>=3.0.3 <4.0.0'