Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
android:theme="@style/Theme.Todakun">
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Todakun">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package com.kikidan.designsystem.component.chat

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalRippleConfiguration
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.kikidan.designsystem.R
import com.kikidan.designsystem.theme.TodakunColor
import com.kikidan.designsystem.theme.TodakunTheme
import com.kikidan.designsystem.theme.TodakunTypography

@Composable
fun TodakunChatCalenderActionCard(
category: String,
dateText: String,
buttonLabel: String,
onButtonClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier =
modifier
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(TodakunColor.white)
.border(1.dp, TodakunColor.coolGray300, RoundedCornerShape(12.dp))
.padding(20.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = "[$category]",
style = TodakunTypography.body2SemiBold,
color = TodakunColor.gray975,
)
Text(
text = dateText,
style = TodakunTypography.body3Regular,
color = TodakunColor.gray800,
)
}

Spacer(modifier = Modifier.height(20.dp))

CalendarActionButton(
text = buttonLabel,
painter = painterResource(id = R.drawable.ic_calendar_add),
onClick = onButtonClick,
modifier = Modifier.fillMaxWidth(),
)
}
}

@Composable
private fun CalendarActionButton(
text: String,
painter: Painter,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()

val pressedOverlay = TodakunColor.gray975.copy(alpha = 0.16f)
val containerColor =
if (isPressed) pressedOverlay.compositeOver(TodakunColor.primary600) else TodakunColor.primary600

CompositionLocalProvider(LocalRippleConfiguration provides null) {
Button(
onClick = onClick,
modifier = modifier,
shape = RoundedCornerShape(12.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = containerColor,
contentColor = TodakunColor.white,
),
contentPadding = PaddingValues(horizontal = 20.dp, vertical = 16.dp),
interactionSource = interactionSource,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painter,
contentDescription = null,
modifier = Modifier.size(20.dp),
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = text,
style = TodakunTypography.body3SemiBold,
)
}
}
}
}

@Preview(showBackground = true)
@Composable
private fun TodakunChatCalenderActionCardPreview() {
TodakunTheme {
TodakunChatCalenderActionCard(
category = "계약 · 이사",
dateText = "2026 . 7 . 25 (토)",
buttonLabel = "내 캘린더에 추가하기",
onButtonClick = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.kikidan.designsystem.component.chat

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
Expand All @@ -14,6 +15,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
Expand Down Expand Up @@ -53,7 +55,11 @@ fun TodakunChatHeader(
Modifier
.align(Alignment.CenterStart)
.size(20.dp)
.clickable(onClick = onCloseClick),
.clickable(
onClick = onCloseClick,
indication = null,
interactionSource = remember { MutableInteractionSource() },
),
)

Row(
Expand Down Expand Up @@ -90,7 +96,11 @@ fun TodakunChatHeader(
modifier =
Modifier
.size(20.dp)
.clickable(onClick = onChatIconClick),
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = onChatIconClick,
),
)
Icon(
painter = painterResource(id = R.drawable.ic_notes),
Expand All @@ -100,7 +110,11 @@ fun TodakunChatHeader(
Modifier
.padding(start = 16.dp)
.size(20.dp)
.clickable(onClick = onNotesIconClick),
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = onNotesIconClick,
),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.kikidan.designsystem.component.chat
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
Expand All @@ -20,6 +21,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand Down Expand Up @@ -142,7 +144,12 @@ private fun SendMessageButton(
.background(
color = if (isFilled) TodakunColor.primary600 else TodakunColor.gray50,
shape = CircleShape,
).clickable(enabled = isFilled, onClick = onSendClick),
).clickable(
enabled = isFilled,
onClick = onSendClick,
indication = null,
interactionSource = remember { MutableInteractionSource() },
),
contentAlignment = Alignment.Center,
) {
Icon(
Expand Down
9 changes: 9 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_calendar_add.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="16dp"
android:viewportWidth="14"
android:viewportHeight="15.625">
<path
android:fillColor="#FF000000"
android:pathData="M10 0C10.3452 -2.26324e-08 10.625 0.279822 10.625 0.625V1.625H12C13.1046 1.625 14 2.52043 14 3.625V13.625C14 14.6607 13.2128 15.512 12.2041 15.6143L12 15.625H2L1.7959 15.6143C0.854346 15.5188 0.1062 14.7707 0.0107422 13.8291L0 13.625V3.625C0 2.52043 0.895431 1.625 2 1.625H3.375V0.625C3.375 0.279822 3.65482 2.26324e-08 4 0C4.34518 -2.26324e-08 4.625 0.279822 4.625 0.625V1.625H9.375V0.625C9.375 0.279822 9.65482 2.26324e-08 10 0ZM1.25 13.625C1.25 14.0392 1.58579 14.375 2 14.375H12C12.4142 14.375 12.75 14.0392 12.75 13.625V6.75H1.25V13.625ZM6.67871 8.08887C6.90888 7.95079 7.20786 7.97404 7.41504 8.1582L9.41504 9.93555C9.6728 10.1649 9.69601 10.5604 9.4668 10.8184C9.23755 11.0759 8.84284 11.0989 8.58496 10.8701L7.625 10.0156V12.625C7.62492 12.97 7.34501 13.2499 7 13.25C6.65487 13.25 6.37508 12.9701 6.375 12.625V10.0156L5.41504 10.8701C5.15724 11.0991 4.76254 11.0757 4.5332 10.8184C4.30388 10.5604 4.32697 10.1649 4.58496 9.93555L6.58496 8.1582L6.67871 8.08887ZM2 2.875C1.58579 2.875 1.25 3.21079 1.25 3.625V5.5H12.75V3.625C12.75 3.21079 12.4142 2.875 12 2.875H2Z" />
</vector>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions core/designsystem/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,36 @@
<string name="bottom_nav_todak">토닥이</string>
<string name="bottom_nav_lucky_action">행운 액션</string>
<string name="bottom_nav_my">마이</string>

<!--Todak Chat-->
<string name="chat_header_title">토닥이</string>
<string name="chat_entry_question">오늘은 어떤게 궁금해?</string>
<string name="chat_thinking_description">토닥이가 생각하고 있어요</string>
<string name="chat_thinking_label">생각 중</string>
<string name="chat_greeting_close_description">그리팅 닫기</string>
<string name="chat_default_error">알 수 없는 에러가 발생했어요. 잠시 후 다시 시도해주세요</string>
<string name="chat_charactor_content_description">토닥이 사진</string>

<!-- 카테고리 팝업 문구 -->
<string name="chat_greeting_relationship_title">관계운을 알려줄게!</string>
<string name="chat_greeting_relationship_body">친구, 가족, 동료 등\n 고민되는 게 있으면 전부 나한테 물어봐.</string>
<string name="chat_greeting_love_title">연애운을 알려줄게!</string>
<string name="chat_greeting_love_body">짝사랑, 썸, 연애 등\n 남한테 말하지 못하는 비밀을 나한테 털어놔.</string>
<string name="chat_greeting_achievement_title">성취운을 알려줄게!</string>
<string name="chat_greeting_achievement_body">커리어, 학업, 목표 등\n 궁금한 점이나 고민은 전부 나한테 물어봐.</string>
<string name="chat_greeting_money_title">금전운을 알려줄게!</string>
<string name="chat_greeting_money_body">수입, 지출, 재테크 등\n 돈의 흐름에 대해서 나한테 물어봐.</string>
<string name="chat_greeting_health_title">건강운을 알려줄게!</string>
<string name="chat_greeting_health_body">컨디션, 습관, 마음가짐 등\n 요즘 신경 쓰이는 걸 나한테 말해봐.</string>

<!-- Splash -->
<string name="chat_splash_brand_primary">토닥운</string>
<string name="chat_splash_brand_secondary">AI 토닥이</string>
<string name="chat_splash_tagline">토닥이에게\n궁금한 운세를 다 물어봐!</string>

<!-- History -->
<string name="chat_history_title">대화 히스토리</string>
<string name="chat_history_empty">아직 토닥이와 나눈 대화가 없어요.</string>
<string name="chat_history_new_chat">새 채팅</string>

</resources>
53 changes: 53 additions & 0 deletions feature/chat/src/main/java/com/kikidan/chat/ChatRoute.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.kikidan.chat

import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import com.kikidan.chat.model.ChatSideEffect
import com.kikidan.chat.screen.ChatScreen
import com.kikidan.chat.screen.ChatSplashScreen
import com.kikidan.designsystem.R
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect

@Composable
fun ChatRoute(
conversationId: String?,
onCloseClick: () -> Unit,
onNavigateToHistory: () -> Unit,
snackbarHostState: SnackbarHostState,
modifier: Modifier = Modifier,
viewModel: ChatViewModel = hiltViewModel(),
) {
val state by viewModel.collectAsState()
val defaultErrorMessage = stringResource(R.string.chat_default_error)
val showSplash = state.quota == null

LaunchedEffect(Unit) { viewModel.load(conversationId) }

viewModel.collectSideEffect { effect ->
when (effect) {
is ChatSideEffect.ShowStreamingErrorMessage -> snackbarHostState.showSnackbar(effect.message)
is ChatSideEffect.Error -> snackbarHostState.showSnackbar(defaultErrorMessage)
}
}

if (showSplash) {
ChatSplashScreen()
} else {
ChatScreen(
state = state,
onInputChange = viewModel::onInputChange,
onSendClick = viewModel::onSendClick,
onSuggestionClick = viewModel::onSuggestionClick,
onNewConversationClick = viewModel::startNewConversation,
onCloseClick = onCloseClick,
onHistoryClick = onNavigateToHistory,
modifier = modifier,
)
}
}
Loading
Loading