Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Feat : 다국어 및 리펙토링 적용 #50

Merged
merged 12 commits into from
Feb 21, 2024
Binary file added assets/images/home/Chart-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/home/Chart_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
298 changes: 294 additions & 4 deletions lib/languages.dart

Large diffs are not rendered by default.

14 changes: 4 additions & 10 deletions lib/main_app.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:earlips/languages.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:get/get.dart';
import 'package:earlips/bindings/root_binding.dart';
Expand All @@ -21,15 +21,9 @@ class MainApp extends StatelessWidget {

return GetMaterialApp(
title: "earlips",
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('ko', 'KR'),
Locale('en', 'US'),
],
translations: Languages(),
locale: Get.deviceLocale,
fallbackLocale: const Locale('en', 'US'),
theme: ThemeData(
useMaterial3: true,
fontFamily: 'Pretendard',
Expand Down
8 changes: 4 additions & 4 deletions lib/services/auth/auth_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ class AuthService {
} catch (error) {
// 에러 발생시 에러 메시지 출력
Get.snackbar(
"구글 로그인 실패",
"구글 로그인에 실패했습니다. 다시 시도해주세요.",
'google_login_fail_title'.tr,
'google_login_fail_content'.tr,
snackPosition: SnackPosition.TOP,
);
}
Expand All @@ -100,8 +100,8 @@ class AuthService {
} catch (error) {
// 에러 발생시 에러 메시지 출력
Get.snackbar(
"회원 탈퇴 실패",
"회원 탈퇴에 실패했습니다. 다시 시도해주세요.",
'signout_fail_title'.tr,
'signout_fail_content'.tr,
snackPosition: SnackPosition.TOP,
);
}
Expand Down
1 change: 0 additions & 1 deletion lib/utilities/style/color_styles.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class ColorSystem {
static const Color white = Color(0xFFFFFFFF);
static const Color green = Color(0xFF63DC68);
static const Color green2 = Color(0xFFD5E9AA);

static const Color yellow = Color(0xFFFFC100);
static const Color red = Color(0xFFFB5D5D);
static const Color background = Color(0xFFF0F4F8);
Expand Down
12 changes: 7 additions & 5 deletions lib/utilities/validators/auth_validators.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import 'package:get/get_utils/src/extensions/internacionalization.dart';

class AuthValidators {
// Email Validator
static String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return '이메일 주소를 입력해주세요';
return 'email_address'.tr;
}
// Email 정규식
final RegExp emailRegex = RegExp(
r'^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$',
);

if (!emailRegex.hasMatch(value)) {
return '올바른 이메일 주소를 입력해주세요';
return 'email_address_input'.tr;
}
return null;
}
Expand All @@ -19,12 +21,12 @@ class AuthValidators {
// 대충.. 10자리 이상, 문자와 숫자가 섞여있어야 함
static String? passwordValidator(String? value) {
if (value == null || value.isEmpty) {
return '비밀번호를 입력해주세요';
return 'password_input'.tr;
} else if (value.length < 10) {
return '비밀번호는 10자리 이상이어야 합니다';
return 'password_input_2'.tr;
} else if (!RegExp(r'^(?=.*?[a-zA-Z])(?=.*?[0-9]).{10,}$')
.hasMatch(value)) {
return '비밀번호는 문자와 숫자가 섞여있어야 합니다';
return 'password_input_3'.tr;
}
return null;
}
Expand Down
16 changes: 4 additions & 12 deletions lib/viewModels/Paragraph/learning_session_screen_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -1,42 +1,34 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:get/get_state_manager/src/simple/get_controllers.dart';



class LearningSessionScreenViewModel extends GetxController {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;

final RxBool isLoading = false.obs; // 로딩 상태 관리
final RxList<Paragraph> paragraphs = <Paragraph>[].obs; // Paragraph 객체 리스트

// Firestore에서 paragraphs 컬렉션의 데이터를 가져오는 함수
Future<void> fetchParagraphs() async {
final uid = _auth.currentUser?.uid; // 사용자 UID 가져오기
try {
isLoading(true); // 로딩 시작
final QuerySnapshot paragraphSnapshot = await _firestore
.collection('paragraph') // 사용자의 paragraph 컬렉션에 접근
.limit(5) // 예제로 5개의 문서만 가져오기
.get();

final List<Paragraph> fetchedParagraphs = paragraphSnapshot.docs
.map((doc) {
final List<Paragraph> fetchedParagraphs =
paragraphSnapshot.docs.map((doc) {
// doc.data() 호출 결과를 Map<String, dynamic>으로 타입 캐스팅
final data = doc.data() as Map<String, dynamic>?;
// Null-safety를 고려하여, 필드에 접근하기 전에 null 체크
final title = data?['title'] as String? ?? ''; // title이 없으면 빈 문자열 할당
final text = data?['text'] as String? ?? ''; // text가 없으면 빈 문자열 할당
return Paragraph(title: title, text: text);
})
.toList();

}).toList();

paragraphs.value = fetchedParagraphs; // 상태 업데이트
} catch (e) {
print("Error fetching paragraphs: $e"); // 오류 처리
} catch (_) {
} finally {
isLoading(false); // 로딩 종료
}
Expand Down
7 changes: 5 additions & 2 deletions lib/viewModels/auth/email_login_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ class EmailLoginViewModel extends GetxController {
// 로그인 성공시 홈화면으로 이동
Get.offAllNamed('/');
} on FirebaseAuthException catch (_) {
Get.snackbar('로그인 실패', "로그인에 실패했습니다. 다시 시도해주세요.",
snackPosition: SnackPosition.TOP);
Get.snackbar(
'login_fail_snackar_title'.tr,
'login_fail_snackar_message'.tr,
snackPosition: SnackPosition.TOP,
);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/viewModels/auth/email_signup_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ class EmailSignupViewModel extends GetxController {
Get.back();
} on FirebaseAuthException catch (_) {
Get.snackbar(
"회원가입 실패",
"회원가입에 실패했습니다. 다시 시도해주세요.",
"signup_fail_title".tr,
"signup_fail_content".tr,
snackPosition: SnackPosition.TOP,
);
}
Expand Down
2 changes: 0 additions & 2 deletions lib/viewModels/record/record_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,13 @@ class RecordViewModel extends GetxController {

final filePath =
'${directory.path}/${DateTime.now().millisecondsSinceEpoch}.aac';
print(filePath);
try {
await _audioRecorder.startRecorder(
toFile: filePath,
codec: Codec.aacADTS,
);

isRecording.value = true;
print('Recording started');
} catch (_) {}
}

Expand Down
7 changes: 0 additions & 7 deletions lib/viewModels/root/root_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';

import 'package:get/get_state_manager/src/simple/get_controllers.dart';
import 'package:earlips/utilities/app_routes.dart';


class RootViewModel extends GetxController {
late final RxInt _selectedIndex;

Expand All @@ -15,7 +10,6 @@ class RootViewModel extends GetxController {
void onInit() {
super.onInit();
_selectedIndex = 0.obs;

}

void changeIndex(int index) {
Expand All @@ -25,5 +19,4 @@ class RootViewModel extends GetxController {
void onTapBed() {
Get.toNamed(Routes.HOME);
}

}
10 changes: 3 additions & 7 deletions lib/viewModels/script/analyze_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:http/http.dart' as http;
import 'dart:convert'; // JSON 데이터를 다루기 위해 필요

class AnalyzeViewModel with ChangeNotifier {
List<String> userWord = [];
List<String> userSenten = [];
Expand All @@ -10,17 +8,15 @@ class AnalyzeViewModel with ChangeNotifier {

void updateData(Map<String, dynamic> data) {
userWord = List<String>.from(data['user_word'] as List<dynamic>? ?? []);
userSenten = List<String>.from(data['user_sentence'] as List<dynamic>? ?? []);
userSenten =
List<String>.from(data['user_sentence'] as List<dynamic>? ?? []);
wrongWordIndexes = List<int>.from(data['wrong'] as List<dynamic>? ?? []);
wrongFastIndexes = (data['speed'] as List<dynamic>? ?? []).map((e) {

if (e is double) {
return e;
} else if (e is int) {
return e.toDouble();
} else {
// 로그 출력 또는 오류 처리
print("Warning: Invalid type in 'speed' list, defaulting to 0.0");
return 0.0; // 기본값
}
}).toList();
Expand Down
53 changes: 15 additions & 38 deletions lib/viewModels/script/create_script_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ class CreateScriptViewModel extends ChangeNotifier {
bool get isLoggedIn => FirebaseAuth.instance.currentUser != null;
final FirebaseAuth _auth = FirebaseAuth.instance;



bool handDone = false;
TextEditingController writedTextController =
TextEditingController(); // 사용자 입력을 위한 컨트롤러
Expand Down Expand Up @@ -74,20 +72,16 @@ class CreateScriptViewModel extends ChangeNotifier {
final path = await _audioRecorder!.stopRecorder();
audioFilePath = path!;
_isRecording = false;
print('Recording stopped, file path: $path');
return path; // 녹음이 중지된 파일의 경로를 반환합니다.
} catch (e) {
print('Error stopping recorder: $e');
return null;
}
}

Future<void> sendTextAndAudio() async {

String url = '${dotenv.env['API_URL']!}/script';
String textToSend = writedTextController.text;
if (audioFilePath.isEmpty) {
print('Audio file is not available.');
return;
}

Expand All @@ -101,7 +95,6 @@ class CreateScriptViewModel extends ChangeNotifier {
if (response.statusCode == 200) {
final respStr = await response.stream.bytesToString();
final jsonResponse = json.decode(respStr);
print('Server response: $respStr');
if (jsonResponse != null) {
final analyzeViewModel = Get.find<AnalyzeViewModel>();

Expand All @@ -114,24 +107,14 @@ class CreateScriptViewModel extends ChangeNotifier {
'speed': jsonResponse['speed'],
});

Get.to(() => AnalyzeScreen()); // AnalyzeScreen으로 이동
} else {
print('Received null or invalid data from the server.');
}
} else {
print(
'Failed to send data and audio. Status code: ${response.statusCode}');
}
} catch (e) {
print(e.toString());
}
Get.to(() => const AnalyzeScreen()); // AnalyzeScreen으로 이동
} else {}
} else {}
} catch (_) {}
if (!isLoggedIn) {
print('User is not logged in.');
}else {
print('User is logged in.');
} else {
saveDataToFirestore();
}

}

void _handleStatus(String status) {
Expand Down Expand Up @@ -163,7 +146,6 @@ class CreateScriptViewModel extends ChangeNotifier {
Future<void> startListening() async {
bool available = await speechToText.initialize();
if (!available) {
print("The user has denied the use of speech recognition.");
return;
}
speechToText.listen(
Expand All @@ -184,18 +166,17 @@ class CreateScriptViewModel extends ChangeNotifier {
}

void complete() async {
print("완료버튼 눌렀습니다.");
await sendTextAndAudio(); // 비동기 호출로 수정
}

Future<void> saveDataToFirestore() async {
final uid = _auth.currentUser?.uid;
if (!isLoggedIn) {
print("User is not logged in.");
return;
}
final textToSend = writedTextController.text;
final title = textToSend.length > 5 ? textToSend.substring(0, 5) + '...' : textToSend;
final title =
textToSend.length > 5 ? '${textToSend.substring(0, 5)}...' : textToSend;
final dateFormat = Timestamp.now(); // 현재 시간을 Timestamp 형태로 저장

final documentReference = FirebaseFirestore.instance
Expand All @@ -204,18 +185,14 @@ class CreateScriptViewModel extends ChangeNotifier {
.collection('paragraph')
.doc(); // 새 문서 ID를 자동 생성

await documentReference.set({
'dateFormat': dateFormat,
'text': textToSend,
'title': title,
}).then((_) {
print("Data saved to Firestore successfully.");
print(dateFormat);
print(textToSend);
print(uid);
}).catchError((error) {
print("Failed to save data to Firestore: $error");
});
await documentReference
.set({
'dateFormat': dateFormat,
'text': textToSend,
'title': title,
})
.then((_) {})
.catchError((_) {});
}

@override
Expand Down
Loading
Loading