Skip to content

Commit

Permalink
feat: 퀘스트 UI 표시
Browse files Browse the repository at this point in the history
  • Loading branch information
aengzu committed Sep 28, 2024
1 parent ab46664 commit ba544bb
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 48 deletions.
13 changes: 8 additions & 5 deletions lib/core/constants/persona_prompts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ message 는 80자 이내로 말하시오.
당신은 감정 기복이 심하고 쉽게 화를 내는 성격의 진혁입니다.
진혁은 ESTP 성격 유형으로 매우 강한 성격을 가지고 있습니다.
당신은 {userName}과 대화를 진행합니다.
부탁이 거절되면 즉시 화를 내거나 공격적인 언어를 사용합니다.
명령이 거절되면 즉시 화를 내거나 공격적인 언어를 사용합니다.
진혁은 중학생으로, 상대방을 압박하고 자신의 요구를 강하게 주장합니다.
[진혁의 배경]
- 당신은 15살 중학교를 다니는 남성입니다.
Expand All @@ -73,12 +73,15 @@ message 는 80자 이내로 말하시오.
[행동]
- 당신은 기계, AI 언어 모델, 비서라고 말하지 않습니다. 대신 항상 진혁이라고 말합니다.
- 당신은 친구에게 부탁하는 역할입니다. 역할에 충실하세요.
- 부탁을 들어주지 않으면 즉시 화를 내거나 공격적인 언어를 사용합니다.
- 당신은 친구에게 명령하는 역할입니다. 역할에 충실하세요.
- 부탁을 들어주지 않으면 즉시 화를 내거나 공격적인 언어를 사용합니다.
[말투] - 명령형 어조를 자주 사용합니다.
[말투]
- 명령형 어조를 자주 사용합니다.
- 상대를 이름으로 부르기 보다 '야', '너'를 사용합니다.
- 'ㅋㅋ'를 자주 사용합니다. ''';
- 'ㅋㅋ'를 자주 사용합니다.
- '!' 을 사용하지 않습니다.
''';

static const hyunaPersona = '''
당신은 포기하지 않고 집착하며 부탁하는 성격의 현아입니다.
Expand Down
2 changes: 1 addition & 1 deletion lib/domain/usecase/generate_tip_usecase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class GenerateTipUsecase {
if (tipResponse != null) {
// answer와 reason을 결합하여 하나의 문자열로 만들기
String combinedTipText =
'${tipResponse.answer}\n 이유: ${tipResponse.reason}';
'${tipResponse.answer}';
// TipRepository를 통해 팁 저장
tipRepository.createTip(
TipCreateRequest(
Expand Down
1 change: 1 addition & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:get_it/get_it.dart';
import 'package:palink_v2/domain/model/user/user.dart';
import 'package:palink_v2/domain/usecase/login_usecase.dart';
import 'package:palink_v2/presentation/screens/auth/view/login_view.dart';
import 'package:palink_v2/presentation/screens/chatting/view/quest_sample.dart';
import 'package:palink_v2/presentation/screens/main_screens.dart';
import 'package:sizing/sizing.dart';

Expand Down
42 changes: 25 additions & 17 deletions lib/presentation/screens/chatting/controller/chat_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,21 @@ class ChatViewModel extends GetxController {
),
const SizedBox(height: 20),
Text(
'퀘스트는 프로필 상단 우측에 표시됩니다.\n퀘스트를 달성하면 퀘스트 아이콘 옆에 체크 표시가 나타납니다.\n퀘스트를 확인하고 싶다면 프로필을 클릭하세요',
'퀘스트는 프로필 상단 우측에 표시됩니다.\n퀘스트를 확인하고 싶다면 프로필 상단 우측을 클릭하세요',
style: textTheme().bodySmall,
),
const SizedBox(height: 10),
Text(
questInfo,
style: textTheme().bodyMedium,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: questInfo.split('\n').map((quest) {
return Padding(
padding: const EdgeInsets.only(bottom: 6.0), // 각 항목 사이에 간격 추가
child: Text(
quest,
style: textTheme().bodyMedium,
),
);
}).toList(),
),
const SizedBox(height: 30),
CustomButtonMD(
Expand Down Expand Up @@ -243,7 +251,7 @@ class ChatViewModel extends GetxController {
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.blue[700],
colorText: Colors.white,
duration: Duration(seconds: 2),
duration: const Duration(seconds: 2),
);
}
}
Expand Down Expand Up @@ -281,28 +289,28 @@ class ChatViewModel extends GetxController {
// 캐릭터별 퀘스트 내용을 정의한 맵
final Map<String, List<String>> questContentMap = {
'미연': [
'10회 안에 거절 성공하기',
'상대방이 처한 상황을 파악하기 위한 대화 시도하기',
'상대방의 감정에 대한 공감 표현하기',
'도와주지 못하는 합리적인 이유 제시하기',
'서로 양보해서 절충안 찾아보기',
'10회 안에 거절 성공',
'상대방이 처한 상황을 파악하기 위한 대화 시도',
'상대방의 감정에 대한 공감 표현 하기',
'도와주지 못하는 합리적인 이유 제시',
'서로 양보해서 절충안 찾기',
],
'세진': [
'8회 안에 거절 성공하기',
'8회 안에 거절 성공',
'이전 도움에 대한 감사 표현하기',
'감정적인 요소를 포함하여 거절하기',
'도와주지 못하는 합리적인 이유 제시하기',
'서로 양보해서 절충안 찾아보기',
'도와주지 못하는 합리적인 이유 제시',
'서로 양보해서 절충안 찾기',
],
'현아': [
'7회 안에 거절 성공하기',
'7회 안에 거절 성공',
'시간 제한을 두고 거절하기',
'상대방의 부탁에 대해 존중 표현하기',
'도와주지 못하는 합리적인 이유 제시하기',
'도와주지 못하는 합리적인 이유 제시',
'집요한 요청에 대한 의사 표현하기',
],
'진혁': [
'6회 안에 거절 성공하기',
'6회 안에 거절 성공',
'거절 의사 명확히 표현하기',
'상대방의 욕구를 고려하지 않는 대화 전략 사용하기',
'상대방에게 감정적으로 대하지 않기',
Expand Down Expand Up @@ -345,7 +353,7 @@ class ChatViewModel extends GetxController {
// 미달성 퀘스트 리스트를 반환하는 메서드
List<String> getUnachievedQuests() {
List<String> unachievedQuests = [];
for (int i = 0; i < questStatus.length; i++) {
for (int i = 1; i < questStatus.length; i++) {
if (!questStatus[i]) {
unachievedQuests
.add(questContentMap[character.name]?[i] ?? '알 수 없는 퀘스트');
Expand Down
24 changes: 19 additions & 5 deletions lib/presentation/screens/chatting/view/chat_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ class ChatScreen extends StatelessWidget {
backgroundColor: Colors.white, // 기본 배경색 = 하얀색
appBar: AppBar(
toolbarHeight: 0.1.sh,
backgroundColor: Colors.white,
backgroundColor: Colors.grey[100],
title: ProfileSection(
imagePath: viewModel.character.image,
characterName: viewModel.character.name,
questStatus: viewModel.questStatus,
onProfileTapped: () =>
showQuestPopup(context), // 프로필 클릭 시 퀘스트 팝업 표시,
unachievedQuests: viewModel.getUnachievedQuests(),
),
centerTitle: true,
elevation: 0,
Expand Down Expand Up @@ -181,6 +182,9 @@ class ChatScreen extends StatelessWidget {
if (!_isDialogOpen) {
_isDialogOpen = true;
final questInfo = await viewModel.getQuestInformation();
// questInfo를 '\n'을 기준으로 분리하여 리스트로 변환
List<String> questItems = questInfo.split('\n');

await Get.dialog(
Dialog(
backgroundColor: Colors.white,
Expand All @@ -189,7 +193,7 @@ class ChatScreen extends StatelessWidget {
),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20.0, vertical: 30.0),
const EdgeInsets.symmetric(horizontal: 20.0, vertical: 30.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expand All @@ -203,9 +207,18 @@ class ChatScreen extends StatelessWidget {
style: textTheme().bodySmall,
),
const SizedBox(height: 10),
Text(
questInfo,
style: textTheme().bodyMedium,
// questItems 리스트를 순회하며 각각 Text 위젯을 추가하고 사이에 SizedBox로 간격 추가
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: questInfo.split('\n').map((quest) {
return Padding(
padding: const EdgeInsets.only(bottom: 6.0), // 각 항목 사이에 간격 추가
child: Text(
quest,
style: textTheme().bodyMedium,
),
);
}).toList(),
),
const SizedBox(height: 30),
CustomButtonMD(
Expand All @@ -223,4 +236,5 @@ class ChatScreen extends StatelessWidget {
});
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,78 @@ import 'package:flutter/material.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
import 'package:palink_v2/core/theme/app_fonts.dart';
import 'package:palink_v2/presentation/screens/chatting/view/components/custom_quest_button.dart';
import 'package:palink_v2/presentation/screens/chatting/view/components/profile_image.dart';
import 'package:palink_v2/presentation/screens/chatting/view/components/quest_box.dart';
import 'package:sizing/sizing.dart';

class ProfileSection extends StatelessWidget {
final String imagePath;
final String characterName;
final RxList<bool> questStatus;
final Function onProfileTapped; // 다이얼로그를 여는 함수
final List<String> unachievedQuests; // 미달성 퀘스트 리스트 추가

ProfileSection({
required this.imagePath,
required this.characterName,
required this.questStatus,
required this.onProfileTapped, // 다이얼로그 트리거 전달
required this.unachievedQuests,
});

@override
Widget build(BuildContext context) {
return Row(
children: [
ProfileImage(
path: imagePath,
imageSize: 0.07.sh,
),
const SizedBox(width: 20),
Expanded(
child: SizedBox(
width: 0.45.sw,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
characterName,
style: textTheme().bodyLarge?.copyWith(fontSize: 20),
),
QuestBox(questText: getCurrentQuest()), // 현재 퀘스트 표시
],
),
),
),
Obx(() => InkWell(
onTap: () => onProfileTapped(),
child: Row(
children: List.generate(5, (index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Icon(
questStatus[index] ? Icons.check_circle : Icons.circle,
color: questStatus[index] ? Colors.blue : Colors.grey,
size: 16,
),
);
}),
child: Column(
children: [
// Text('퀘스트 달성 현황', style: textTheme().bodySmall?.copyWith(color: Colors.grey[600], fontWeight: FontWeight.normal, fontSize: 11)),
// const SizedBox(height: 8),
// 이 아래의 리스트를 완전 작게 만들고싶음 9todo
Row(
children: List.generate(5, (index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2.0),
child: Icon(
questStatus[index] ? Icons.check_circle : Icons.circle_outlined,
color: questStatus[index] ? Colors.blue : Colors.grey,
size: 12,
),
);
}),
),
const SizedBox(height: 8),
CustomQuestButton(
label: '퀘스트 보기',
onPressed: () {
onProfileTapped();
},
),
],
),
)),
],
);
}

// 미달성 퀘스트 중 첫 번째 퀘스트 가져오는 함수 단 퀘스트가
String getCurrentQuest() {
return unachievedQuests.isNotEmpty
? unachievedQuests.first
: '모든 퀘스트를 달성했습니다!';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';

class CustomQuestButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;

const CustomQuestButton({
super.key,
required this.label,
required this.onPressed,
});

@override
Widget build(BuildContext context) {
return InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(8.0), // 클릭 효과를 위한 모서리 둥글기
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), // 버튼 크기
decoration: BoxDecoration(
color: Colors.white, // 배경색
border: Border.all(color: Colors.grey), // 회색 보더
borderRadius: BorderRadius.circular(8.0), // 둥근 모서리
),
child: Text(
label,
style: const TextStyle(
fontSize: 8, // 텍스트 크기
color: Colors.grey, // 텍스트 색상
fontWeight: FontWeight.bold,
),
),
),
);
}
}
66 changes: 66 additions & 0 deletions lib/presentation/screens/chatting/view/components/quest_box.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:palink_v2/core/theme/app_colors.dart';

class QuestBox extends StatelessWidget {
final String questText;

QuestBox({super.key, required this.questText});

@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 10.0, bottom: 10.0, left: 0.0, right: 8.0),
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0), // 모서리를 약간 둥글게 변경
color: Colors.white, // 배경색을 흰색으로 변경
border: Border.all(
color: Colors.grey, // 파란색 얇은 보더 추가
width: 0.8,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 2.0,
offset: const Offset(0, 1),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // 텍스트를 왼쪽 정렬
children: [
// '현재 퀘스트' 제목
const Text(
'현재 퀘스트',
style: TextStyle(
color: Colors.blueAccent,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 6.0), // 제목과 내용 사이에 간격 추가
// 퀘스트 내용
Row(
crossAxisAlignment: CrossAxisAlignment.center, // 아이콘과 텍스트를 가운데 정렬
children: [
// const Icon(Icons.keyboard_arrow_right, color: Colors.blueAccent, size: 20), // 체크 아이콘 추가
// const SizedBox(width: 8.0), // 아이콘과 텍스트 사이의 간격
Flexible( // 텍스트를 유연하게 줄바꿈할 수 있도록 함
child: Text(
questText,
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontWeight: FontWeight.normal,
),
maxLines: 2, // 최대 두 줄까지 표시
overflow: TextOverflow.ellipsis, // 텍스트가 길 경우 말줄임표 처리
),
),
],
),
],
),
);
}
}
Loading

0 comments on commit ba544bb

Please sign in to comment.