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: language, chart #49

Merged
merged 8 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lib/languages.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:get/get.dart';

class Languages extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'ko_KR': {'title': '제λͺ©'},
'en_US': {'title': 'title'},
'ja_JP': {'title': '鑌名'},
'vi_VN': {'title': 'TiΓͺu đề'},
};
}
23 changes: 10 additions & 13 deletions lib/viewModels/record/record_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ import 'package:path_provider/path_provider.dart';

class RecordViewModel extends GetxController {
final FlutterSoundRecorder _audioRecorder = FlutterSoundRecorder();
bool _isRecorderInitialized = false; // λ…ΉμŒκΈ° μ΄ˆκΈ°ν™” μ—¬λΆ€ : 파일
bool _isRecorderInitialized = false;
final RxBool isRecording = false.obs;
RxString audioFilePath = ''.obs;
RxDouble loudness = 0.0.obs; // Reactive variable for loudness

final RxMap<String, dynamic> response =
<String, dynamic>{}.obs; // Specify the types

@override
void onInit() {
_requestPermission();
_audioRecorder.openRecorder(); // Initialize recorder
_audioRecorder.onProgress!.listen((e) {
loudness.value = e.decibels! / 160; // Example: Normalize decibels
});
super.onInit();
}

Expand Down Expand Up @@ -65,18 +71,13 @@ class RecordViewModel extends GetxController {
}

Future<void> sendTextAndAudio(String content, int type) async {

isRecording.value ? await _stopRecording() : await _startRecording();
update();
if(isRecording.value == true) {
if (isRecording.value == true) {
return;
}
print("λ“€μ–΄μ˜€κΈ΄ 함?");
String url =
'${dotenv.env['API_URL']!}/study/${type == 0 ? 'syllable' : (type == 1 ? 'word' : 'sentence')}';


print(audioFilePath.value);
if (audioFilePath.value.isEmpty) {
return;
}
Expand All @@ -93,14 +94,10 @@ class RecordViewModel extends GetxController {
final jsonResponse = json.decode(respStr);
//
this.response.value = jsonResponse;
print(jsonResponse);
} else {
print('Failed to upload');
}
} catch (e) {}
} else {}
} catch (_) {}
}


@override
void onClose() {
_audioRecorder.closeRecorder();
Expand Down
46 changes: 46 additions & 0 deletions lib/views/word/widget/fail_word_dialog_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:earlips/utilities/style/color_styles.dart';
import 'package:earlips/viewModels/word/word_viewmodel.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

Future<dynamic> FailWordDialogWidget(
WordViewModel wordViewModel, PageController pageController) {
return Get.dialog(
AlertDialog(
title: const Text('ν•™μŠ΅ μ‹€νŒ¨'),
content: const Text('λ‹€μ‹œ ν•œλ²ˆ λ…ΉμŒν•΄μ£Όμ„Έμš”.'),
backgroundColor: ColorSystem.white,
surfaceTintColor: ColorSystem.white,
actions: [
ElevatedButton(
onPressed: () async {
Get.back();
},
child: const Text('λ‹€μ‹œν•˜κΈ°'),
),
ElevatedButton(
onPressed: () async {
// λ§ˆμ§€λ§‰ 단어가 아닐 경우 λ’€λ‘œκ°€κΈ°, λ§ˆμ§€λ§‰ 단어일 경우 ν™ˆμœΌλ‘œ 이동
wordViewModel.currentIndex.value < wordViewModel.wordList.length - 1
? Get.back()
: Get.offAllNamed('/');
// λ‹€μŒ λ‹¨μ–΄λ‘œ λ„˜μ–΄κ°€κΈ°
if (wordViewModel.currentIndex.value <
wordViewModel.wordList.length - 1) {
pageController.animateToPage(
wordViewModel.currentIndex.value + 1,
duration: const Duration(milliseconds: 300),
curve: Curves.ease,
);

// currentIndex 증가
wordViewModel.currentIndex.value =
wordViewModel.currentIndex.value + 1;
}
},
child: const Text('λ‹€μŒ 단어'),
),
],
),
);
}
35 changes: 35 additions & 0 deletions lib/views/word/widget/highlight_mistake_text_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';

class HighlightMistakesTextWidget extends StatelessWidget {
final List<dynamic> userWords; // μ‚¬μš©μžκ°€ λ°œμŒν•œ 단어 리슀트
final List<dynamic> wrongIndices; // 잘λͺ» λ°œμŒν•œ λ‹¨μ–΄μ˜ 인덱슀 리슀트

const HighlightMistakesTextWidget({
super.key,
required this.userWords,
required this.wrongIndices,
});

@override
Widget build(BuildContext context) {
return wrongIndices[0] == -1
? const Text("틀린단어 μ—†μŒ")
: RichText(
text: TextSpan(
children: userWords.asMap().entries.map((entry) {
int idx = entry.key;
String word = entry.value;
bool isWrong = wrongIndices.contains(idx);
return TextSpan(
text: "$word ", // 단어와 곡백을 ν¬ν•¨μ‹œν‚΅λ‹ˆλ‹€.
style: TextStyle(
color: isWrong
? Colors.red
: Colors.black, // 잘λͺ»λœ 뢀뢄은 λΉ¨κ°„μƒ‰μœΌλ‘œ, κ·Έ μ™ΈλŠ” κ²€μ •μƒ‰μœΌλ‘œ ν‘œμ‹œ
),
);
}).toList(),
),
);
}
}
194 changes: 194 additions & 0 deletions lib/views/word/widget/sentence_alert_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import 'package:earlips/utilities/style/color_styles.dart';
import 'package:earlips/viewModels/record/record_viewmodel.dart';
import 'package:earlips/viewModels/word/word_viewmodel.dart';
import 'package:earlips/views/word/widget/highlight_mistake_text_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

Future<dynamic> SentenceAlertWidget(RecordViewModel model,
WordViewModel wordViewModel, PageController pageController) {
return Get.dialog(
AlertDialog(
title: const Text('λ¬Έμž₯ ν…ŒμŠ€νŠΈ κ²°κ³Ό',
style: TextStyle(
color: ColorSystem.black,
fontSize: 20,
)),
surfaceTintColor: ColorSystem.white,
backgroundColor: ColorSystem.white,
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'λ¬Έμž₯',
style: TextStyle(
color: ColorSystem.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Text(
'${model.response['sentence_word'] != null ? model.response['sentence_word'].join(" ") : "λ‹€μ‹œ λ…ΉμŒν•΄μ£Όμ„Έμš”"}',
style: const TextStyle(
color: ColorSystem.black,
fontSize: 14,
),
)
],
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'μ‚¬μš©μž 발음',
style: TextStyle(
color: ColorSystem.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
HighlightMistakesTextWidget(
userWords: model.response['user_word'] ?? [],
wrongIndices: model.response['wrong'] ?? [-1],
),
],
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'ν‹€λ¦° λΆ€λΆ„',
style: TextStyle(
color: ColorSystem.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Text(
'${model.response['wrong'] != null && model.response['wrong'][0] != -1 ? model.response['wrong'].join(", ") : "μ•Œ 수 μ—†μŒ"} 번째 단어',
style: const TextStyle(
color: ColorSystem.black,
fontSize: 14,
),
)
],
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'λ³Όλ₯¨',
style: TextStyle(
color: ColorSystem.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Text('${model.response['loudness'] ?? 'μ•Œ 수 μ—†μŒ'}'),
],
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'변동성',
style: TextStyle(
color: ColorSystem.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Text(
model.response['variance'] == null
? "μ•Œ 수 μ—†μŒ"
: (model.response['variance'] == 1 ? "일정함" : "변동 폭 큼"),
style: const TextStyle(
color: ColorSystem.black,
fontSize: 14,
),
)
],
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'속도',
style: TextStyle(
color: ColorSystem.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Text(
_getSpeedDescription(model.response['speed'] ?? 1),
style: const TextStyle(
color: ColorSystem.black,
fontSize: 14,
),
),
],
)
],
),
),
actions: <Widget>[
TextButton(
child: const Text(
'λ‹€μŒ',
style: TextStyle(
color: ColorSystem.black,
fontSize: 16,
),
),
onPressed: () async {
// λ‹€μŒμœΌλ‘œ λ„˜μ–΄κ°€λŠ” μ½”λ“œ
await wordViewModel.markWordAsDone(wordViewModel
.wordList[wordViewModel.currentIndex.value].wordCard);
wordViewModel.currentIndex.value < wordViewModel.wordList.length - 1
? Get.back()
: Get.offAllNamed('/');

// λ‹€μŒ λ‹¨μ–΄λ‘œ λ„˜μ–΄κ°€κΈ°
if (wordViewModel.currentIndex.value <
wordViewModel.wordList.length - 1) {
pageController.animateToPage(
wordViewModel.currentIndex.value + 1,
duration: const Duration(milliseconds: 300),
curve: Curves.ease,
);
// currentIndex 증가
wordViewModel.currentIndex.value =
wordViewModel.currentIndex.value + 1;
}
},
),
],
),
);
}

String _getSpeedDescription(double speed) {
switch (speed) {
case 0:
return 'μ—„μ²­ 느림';
case 0.5:
return '느림';
case 1:
return '평범';
case 1.5:
return 'μ•½κ°„ 빠름';
case 2:
return 'μ™„μ „ 빠름';
default:
return 'μ•Œ 수 μ—†μŒ'; // 속도 값이 주어진 λ²”μœ„μ— μ—†λŠ” 경우
}
}
Loading
Loading