diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index 21c7b334..d152bdb9 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -7,8 +7,8 @@ object Versions { const val COMPOSE = "1.1.1" const val COMPOSE_ACTIVITY = "1.4.0" const val COMPOSE_CONSTRAINT_LAYOUT = "1.1.0-alpha02" - const val COMPOSE_NAVIGATION = "2.4.2" const val CONSTRAINT_LAYOUT = "2.1.3" + const val COMPOSE_NAVIGATION = "2.4.2" const val FIREBASE_BOM = "29.1.0" const val FIREBASE_CRASHLYTICS = "2.9.0" const val GOOGLE_SERVICE = "4.3.10" @@ -18,6 +18,7 @@ object Versions { const val KAKAO_SDK = "2.10.0" const val KOTLIN = "1.6.10" const val KOTLIN_COROUTINES = "1.5.1" + const val MATERIAL_CALENDAR = "1.4.3" const val LIFECYCLE = "2.4.1" const val OKHTTP = "4.3.1" const val RETROFIT = "2.7.1" diff --git a/buildSrc/src/main/java/app/ModuleDependencies.kt b/buildSrc/src/main/java/app/ModuleDependencies.kt index 07dc3e47..96c09bd2 100644 --- a/buildSrc/src/main/java/app/ModuleDependencies.kt +++ b/buildSrc/src/main/java/app/ModuleDependencies.kt @@ -66,6 +66,11 @@ object ModuleDependencies { Versions.COMPOSE_ACTIVITY, Method.IMPLEMENTATION ), + DependencyInfo( + "androidx.constraintlayout:constraintlayout-compose", + Versions.COMPOSE_CONSTRAINT_LAYOUT, + Method.IMPLEMENTATION + ), DependencyInfo("io.coil-kt:coil-compose", Versions.COIL, Method.IMPLEMENTATION), DependencyInfo( "androidx.navigation:navigation-compose", @@ -144,6 +149,10 @@ object ModuleDependencies { DependencyInfo("javax.inject:javax.inject", Versions.INJECT, Method.IMPLEMENTATION) ) + val materialCalendarView = arrayOf( + DependencyInfo("com.prolificinteractive:material-calendarview", Versions.MATERIAL_CALENDAR, Method.IMPLEMENTATION) + ) + val kakaoSdk = arrayOf( DependencyInfo("com.kakao.sdk:v2-user", Versions.KAKAO_SDK, Method.IMPLEMENTATION) ) diff --git a/gradle.properties b/gradle.properties index 3c5031eb..aa5fda35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,5 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.enableJetifier=true diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index 1ac855b2..54c71060 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { app.ModuleDependencies.hilt.implement(this) app.ModuleDependencies.hiltAndroid.implement(this) app.ModuleDependencies.timber.implement(this) + app.ModuleDependencies.materialCalendarView.implement(this) implementation(platform(app.ModuleDependencies.FIREBASE_BOM)) implementation(app.ModuleDependencies.FIREBASE_ANALYTICS) diff --git a/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/CalendarDecorator.kt b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/CalendarDecorator.kt new file mode 100644 index 00000000..c1632f73 --- /dev/null +++ b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/CalendarDecorator.kt @@ -0,0 +1,168 @@ +package com.yapp.growth.presentation.ui.main.home + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Typeface +import android.text.style.ForegroundColorSpan +import android.text.style.LineBackgroundSpan +import android.text.style.StyleSpan +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.core.content.ContextCompat +import com.prolificinteractive.materialcalendarview.CalendarDay +import com.prolificinteractive.materialcalendarview.DayViewDecorator +import com.prolificinteractive.materialcalendarview.DayViewFacade +import com.prolificinteractive.materialcalendarview.MaterialCalendarView +import com.prolificinteractive.materialcalendarview.spans.DotSpan.DEFAULT_RADIUS +import com.yapp.growth.presentation.R +import com.yapp.growth.presentation.theme.* +import java.util.* + + +class CalendarDecorator { + class TodayDecorator(context: Context) : DayViewDecorator { + private var date = CalendarDay.today() + private val boldSpan = StyleSpan(Typeface.BOLD) + + @SuppressLint("UseCompatLoadingForDrawables") + val drawable = ContextCompat.getDrawable(context, R.drawable.bg_calendar_today) + override fun shouldDecorate(day: CalendarDay): Boolean { + return day == date + } + + override fun decorate(view: DayViewFacade) { + if (drawable != null) { + view.apply { + this.addSpan(object : ForegroundColorSpan(Color.White.toArgb()) {}) + this.setSelectionDrawable(drawable) + this.addSpan(boldSpan) + } + } + } + } + + class SelectDecorator(context: Context, mCalendar: MaterialCalendarView) : DayViewDecorator { + @SuppressLint("UseCompatLoadingForDrawables") + private val mCalendar = mCalendar.currentDate.calendar + private val drawable = ContextCompat.getDrawable(context, R.drawable.bg_calendar_selection) + + override fun shouldDecorate(day: CalendarDay): Boolean { + val calendar = day.calendar + + return calendar.get(Calendar.ERA) == mCalendar.get(Calendar.ERA) + && calendar.get(Calendar.YEAR) == mCalendar.get(Calendar.YEAR) + && calendar.get(Calendar.MONTH) == mCalendar.get(Calendar.MONTH) + } + + override fun decorate(view: DayViewFacade) { + if (drawable != null) { + view.apply { + this.addSpan(object : ForegroundColorSpan(Gray900.toArgb()) {}) + this.setSelectionDrawable(drawable) + } + } + } + } + + class OtherDayDecorator(context: Context, mCalendar: MaterialCalendarView) : DayViewDecorator { + @SuppressLint("UseCompatLoadingForDrawables") + private val mCalendar = mCalendar.currentDate.calendar + private val drawable = ContextCompat.getDrawable(context, R.drawable.bg_calendar_selection) + override fun shouldDecorate(day: CalendarDay): Boolean { + val calendar = day.calendar + + return calendar.get(Calendar.ERA) == mCalendar.get(Calendar.ERA) + && calendar.get(Calendar.YEAR) == mCalendar.get(Calendar.YEAR) + && calendar.get(Calendar.MONTH) != mCalendar.get(Calendar.MONTH) + } + + override fun decorate(view: DayViewFacade) { + if (drawable != null) { + view.apply { + this.addSpan(object : ForegroundColorSpan(Gray300.toArgb()) {}) + this.setSelectionDrawable(drawable) + } + } + } + } + + class SundayDecorator : DayViewDecorator { + private val calendar = Calendar.getInstance() + override fun shouldDecorate(day: CalendarDay): Boolean { + day.copyTo(calendar) + val weekDay = calendar.get(Calendar.DAY_OF_WEEK) + return (weekDay == Calendar.SUNDAY) + } + + override fun decorate(view: DayViewFacade) { + view.addSpan(object : ForegroundColorSpan(SubCoral.toArgb()) {}) + } + } + + // TODO : 일정이 있는 날만 점 데코레이터 찍기 + class DotDecorator : DayViewDecorator { + private var date = CalendarDay.from(2022, 5, 21) + + override fun shouldDecorate(day: CalendarDay): Boolean { + return day == date + } + + override fun decorate(view: DayViewFacade) { + view.addSpan(CustomMultipleDotSpan(4F, color = intArrayOf(SubCoral.toArgb(), SubYellow.toArgb(), MainPurple900.toArgb()))) + } + } + + class CustomMultipleDotSpan : LineBackgroundSpan { + private val radius: Float + private var color = IntArray(0) + + constructor() { + radius = DEFAULT_RADIUS + color[0] = 0 + } + + constructor(color: Int) { + radius = DEFAULT_RADIUS + this.color[0] = 0 + } + + constructor(radius: Float) { + this.radius = radius + color[0] = 0 + } + + constructor(radius: Float, color: IntArray) { + this.radius = radius + this.color = color + } + + override fun drawBackground( + canvas: Canvas, + paint: Paint, + left: Int, + right: Int, + top: Int, + baseline: Int, + bottom: Int, + charSequence: CharSequence, + start: Int, + end: Int, + lineNum: Int + ) { + val total = if (color.size > 5) 5 else color.size + var leftMost = (total - 1) * - 10 + + for (i in 0 until total) { + val oldColor: Int = paint.color + if (color[i] != 0) { + paint.color = color[i] + } + canvas.drawCircle(((left + right) / 2 - leftMost).toFloat(), bottom + 10F , radius, paint) + paint.color = oldColor + leftMost += 20 + } + } + } +} diff --git a/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeContract.kt b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeContract.kt new file mode 100644 index 00000000..629b0293 --- /dev/null +++ b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeContract.kt @@ -0,0 +1,28 @@ +package com.yapp.growth.presentation.ui.main.home + +import com.yapp.growth.base.ViewEvent +import com.yapp.growth.base.ViewSideEffect +import com.yapp.growth.base.ViewState + +class HomeContract { + data class HomeViewState( + val loginState: LoginState = LoginState.LOGIN + ) : ViewState + + // TODO : 유저 아이콘 클릭 시 내 정보 창으로 이동 (정호) + sealed class HomeSideEffect : ViewSideEffect { + object NavigateToInfoScreen : HomeSideEffect() + object NavigateDetailPlanScreen : HomeSideEffect() + object OpenBottomSheet : HomeSideEffect() + } + + sealed class HomeEvent : ViewEvent { + object OnUserImageButtonClicked : HomeEvent() + object OnPlanClicked : HomeEvent() + object OnCalendarDayClicked : HomeEvent() + } + + enum class LoginState { + NONE, LOGIN + } +} diff --git a/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeScreen.kt b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeScreen.kt index 9cc93258..68e1c3c8 100644 --- a/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeScreen.kt +++ b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeScreen.kt @@ -1,25 +1,522 @@ package com.yapp.growth.presentation.ui.main.home -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.hilt.navigation.compose.hiltViewModel +import com.prolificinteractive.materialcalendarview.CalendarDay +import com.prolificinteractive.materialcalendarview.MaterialCalendarView +import com.yapp.growth.presentation.R +import com.yapp.growth.presentation.theme.* +import com.yapp.growth.presentation.ui.main.home.HomeContract.HomeSideEffect +import com.yapp.growth.presentation.util.advancedShadow +import kotlinx.coroutines.flow.collect +import timber.log.Timber + @Composable -fun HomeScreen() { - Surface( +fun HomeScreen( + viewModel: HomeViewModel = hiltViewModel(), +) { + + val viewState by viewModel.viewState.collectAsState() + + // TODO : 이벤트에 따라 사이드 이펙트 적용되게 설정 (정호) + LaunchedEffect(key1 = viewModel.effect) { + viewModel.effect.collect { effect -> + when (effect) { + is HomeSideEffect.NavigateToInfoScreen -> { + // onUserIconClick() + } + is HomeSideEffect.NavigateDetailPlanScreen -> { + // onDetailPlanClick() + } + is HomeSideEffect.OpenBottomSheet -> { + // sheet.show() + } + } + } + } + + Scaffold( + backgroundColor = BackgroundColor1, + topBar = { + HomeUserProfile( + userName = "김정호", + onUserIconClick = { /* TODO */ } + ) + }, modifier = Modifier.fillMaxSize(), - color = Color(0xFFF7F7F8) ) { - Text(text = "Home") + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + Spacer(modifier = Modifier.height(5.dp)) + when (viewState.loginState) { + HomeContract.LoginState.LOGIN -> HomeTodayPlan() + HomeContract.LoginState.NONE -> HomeInduceLogin() + } + Spacer(modifier = Modifier.height(16.dp)) + HomeMonthlyPlan() + Spacer(modifier = Modifier.height(24.dp)) + } + } +} + +// TODO : 클릭 시 내 정보 화면으로 네비게이션 (정호) +@Composable +private fun HomeUserProfile( + userName: String, + onUserIconClick: () -> Unit, +) { + Row( + modifier = Modifier + .height(60.dp) + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton( + modifier = Modifier + .size(30.dp, 30.dp), + onClick = { onUserIconClick() }) { + Image( + painter = painterResource(R.drawable.ic_default_user_image), + contentScale = ContentScale.Crop, + modifier = Modifier + .clip(CircleShape) + .border(1.dp, MainPurple900, CircleShape), + contentDescription = null, + ) + } + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = userName, + style = PlanzTypography.h3, + color = Gray900, + ) + } +} + +// TODO : 약속 수 들어가는 로직 넣기 (정호) +@Composable +fun HomeTodayPlan() { + var expanded by remember { mutableStateOf(false) } + Surface( + color = Color.White, + shape = RoundedCornerShape(12.dp), + modifier = Modifier + .padding(horizontal = 16.dp) + .advancedShadow( + alpha = 0.1f, + cornersRadius = 12.dp, + shadowBlurRadius = 10.dp + ), + ) { + Box( + modifier = Modifier + .wrapContentHeight() + .fillMaxWidth() + .padding(horizontal = 20.dp) + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ), + ) { + Column { + Row( + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(id = R.string.home_today_plan), + color = Color.Black, + style = MaterialTheme.typography.h3, + ) + Spacer(modifier = Modifier.width(8.dp)) + HomeTodayPlanCountText(planCount = 5) + } + Spacer(modifier = Modifier.height(20.dp)) + HomeTodayPlanList(expanded = expanded) + } + IconButton( + modifier = Modifier + .padding(bottom = 8.dp) + .size(12.dp, 6.dp) + .align(Alignment.BottomCenter), + onClick = { expanded = !expanded }) { + Icon( + tint = Color.Unspecified, + imageVector = ( + if (expanded) { + ImageVector.vectorResource(R.drawable.ic_transparent_arrow_top) + } else { + ImageVector.vectorResource(R.drawable.ic_transparent_arrow_bottom) + }), + contentDescription = null, + ) + } + } + } +} + + +@Composable +fun HomeInduceLogin() { + Box( + modifier = Modifier + .height(60.dp) + .fillMaxWidth() + .padding(horizontal = 16.dp) + .clip(RoundedCornerShape(12.dp)) + .background(brush = MainGradient), + contentAlignment = Alignment.Center, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 18.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = stringResource(id = R.string.home_induce_login), + color = Color.White, + style = MaterialTheme.typography.subtitle2, + ) + IconButton( + modifier = Modifier.size(6.dp, 12.dp), + onClick = { /*TODO*/ }, + ) { + Icon( + tint = Color.Unspecified, + imageVector = ImageVector.vectorResource(R.drawable.ic_transparent_arrow_right), + contentDescription = null, + ) + } + } + } +} + +@Composable +fun HomeMonthlyPlan() { + var isCalendarMode by remember { mutableStateOf(true) } + var currentDate: CalendarDay by remember { mutableStateOf(CalendarDay.today()) } + var year: Int by remember { mutableStateOf(currentDate.year) } + var month: Int by remember { mutableStateOf(currentDate.month + 1) } + + Surface( + color = Color.White, + shape = RoundedCornerShape(12.dp), + modifier = Modifier + .padding(horizontal = 16.dp) + .advancedShadow( + alpha = 0.1f, + cornersRadius = 12.dp, + shadowBlurRadius = 10.dp + ), + ) { + Box( + modifier = Modifier + .wrapContentHeight() + .fillMaxWidth() + .padding(horizontal = 20.dp) + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = Modifier.height(20.dp)) + Box( + modifier = Modifier.fillMaxWidth(), + ) { + Text( + text = "${year}년 ${String.format("%02d", month)}월", + style = PlanzTypography.h3, + ) + HomeOutlinedButton( + modifier = Modifier.padding(horizontal = 113.dp), + onClick = { + month-- + if (month == 0) { + year-- + month = 12 + } + currentDate = CalendarDay.from(year, month - 1, 1) + }, + imageVector = ImageVector.vectorResource(R.drawable.ic_arrow_box_left), + ) + HomeOutlinedButton( + modifier = Modifier.padding(horizontal = 139.dp), + onClick = { + month++ + if (month == 13) { + year++ + month = 1 + } + currentDate = CalendarDay.from(year, month - 1, 1) + }, + imageVector = ImageVector.vectorResource(R.drawable.ic_arrow_box_right), + ) + HomeOutlinedButton( + modifier = Modifier.align(alignment = Alignment.CenterEnd), + onClick = { + isCalendarMode = !isCalendarMode + }, + imageVector = if (isCalendarMode) { + ImageVector.vectorResource(R.drawable.ic_list) + } else { + ImageVector.vectorResource(R.drawable.ic_calendar) + }, + ) + } + Spacer(modifier = Modifier.height(12.dp)) + Divider(color = Gray200, thickness = 1.dp) + if (isCalendarMode) { + PlanzCalendar(currentDate) + } else { + Spacer(modifier = Modifier.height(20.dp)) + HomeMonthlyPlanList() + } + } + } + } +} + +// TODO : 추후 공통 컴포넌트로 이동 (정호) +@Composable +fun PlanzCalendar( + currentDate: CalendarDay +) { + val context = LocalContext.current + + AndroidView( + modifier = Modifier + .padding(bottom = 12.dp), + factory = { MaterialCalendarView(it) }, + update = { views -> + views.apply { + this.setOnDateChangedListener { widget, date, selected -> + Timber.d(date.toString()) + } + this.selectionMode = MaterialCalendarView.SELECTION_MODE_SINGLE + this.selectedDate = CalendarDay.today() + this.showOtherDates = MaterialCalendarView.SHOW_OTHER_MONTHS + this.setAllowClickDaysOutsideCurrentMonth(false) + this.currentDate = currentDate + this.isDynamicHeightEnabled = true + this.topbarVisible = false + this.isPagingEnabled = false + this.addDecorator(CalendarDecorator.SelectDecorator(context, this)) + this.addDecorator(CalendarDecorator.SundayDecorator()) + this.addDecorator(CalendarDecorator.OtherDayDecorator(context, this)) + this.addDecorator(CalendarDecorator.TodayDecorator(context)) + this.addDecorator(CalendarDecorator.DotDecorator()) + } + } + ) +} + +@Composable +fun HomeTodayPlanCountText( + planCount: Int = 0 +) { + Box( + modifier = Modifier + .size(28.dp, 18.dp) + .clip(RoundedCornerShape(10.dp)) + .background(color = MainPurple300), + contentAlignment = Alignment.Center, + ) { + Text( + modifier = Modifier.fillMaxHeight(), + text = "$planCount", + color = MainPurple900, + style = MaterialTheme.typography.caption, + ) + } +} + +@Composable +fun HomeTodayPlanList( + expanded: Boolean +) { + Column( + modifier = Modifier.padding(bottom = 36.dp), + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + // TODO : API 연동 + if (expanded) { + for (i in 0 until 3) { + HomeTodayPlanItem() + } + } else { + HomeTodayPlanItem() + } + } +} + +@Composable +fun HomeMonthlyPlanList() { + var expanded by remember { mutableStateOf(false) } + Column( + verticalArrangement = Arrangement.spacedBy(20.dp), + ) { + if (expanded) { + // TODO : 예시 화면 (정호) + for (i in 0 until 10) { + HomeMonthlyPlanItem("그로스 회의회의") + } + } else { + for (i in 0 until 4) { + HomeMonthlyPlanItem("그로스 회의회의") + } + } + } + Spacer(modifier = Modifier.height(24.dp)) + IconButton( + modifier = Modifier + .padding(bottom = 9.dp) + .size(12.dp, 6.dp), + onClick = { expanded = !expanded }, + ) + { + Icon( + tint = Color.Unspecified, + imageVector = + if (expanded) { + ImageVector.vectorResource(R.drawable.ic_transparent_arrow_top) + } else { + ImageVector.vectorResource(R.drawable.ic_transparent_arrow_bottom) + }, + contentDescription = null + ) + } +} + + +// TODO : API 연동 및 매개변수 추가(정호) +@Composable +fun HomeTodayPlanItem() { + Box( + modifier = Modifier + .fillMaxWidth() + .clickable( + onClick = { /*TODO*/ } + ), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + // TODO : 약속 종류에 따라 아이콘 변경 (정호) + Icon( + tint = Color.Unspecified, + imageVector = ImageVector.vectorResource(id = R.drawable.ic_plan_meal), + contentDescription = null, + ) + Spacer(modifier = Modifier.width(16.dp)) + Column { + Text( + text = "6시 30분", + color = Gray500, + style = MaterialTheme.typography.caption, + ) + Text( + text = "돼지파티 약속 외 1건", + color = Color.Black, + style = MaterialTheme.typography.subtitle1, + ) + } + } } } -@Preview +// TODO : API 연동 및 매개변수 추가 (정호) @Composable -fun HomeScreenPreview() { - HomeScreen() -} \ No newline at end of file +fun HomeMonthlyPlanItem(content: String) { + Box( + modifier = Modifier + .fillMaxWidth(), + ) { + Text( + text = "7/15", + color = MainPurple900, + style = MaterialTheme.typography.subtitle2, + modifier = Modifier.padding(horizontal = 5.dp) + ) + Text( + text = content, + color = Color.Black, + style = MaterialTheme.typography.body2, + modifier = Modifier.padding(horizontal = 54.dp) + ) + Text( + text = "6시 30분", + color = Gray500, + style = MaterialTheme.typography.caption, + modifier = Modifier.align(Alignment.CenterEnd) + ) + } +} + +@Composable +fun HomeOutlinedButton( + modifier: Modifier = Modifier, + onClick: () -> Unit, + imageVector: ImageVector, +) { + OutlinedButton( + onClick = { + onClick() + }, + modifier = modifier.size(25.dp), + shape = CircleShape, + border = BorderStroke(1.dp, Color.Transparent), + contentPadding = PaddingValues(0.dp), + colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.DarkGray), + ) { + Icon( + tint = Color.Unspecified, + imageVector = imageVector, + contentDescription = null, + ) + } +} + +@Preview(showBackground = true, widthDp = 360, heightDp = 640) +@Composable +fun PreviewHomeScreen() { + PlanzTheme { + HomeScreen() + } +} diff --git a/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeViewModel.kt b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeViewModel.kt new file mode 100644 index 00000000..959021ba --- /dev/null +++ b/presentation/src/main/java/com/yapp/growth/presentation/ui/main/home/HomeViewModel.kt @@ -0,0 +1,27 @@ +package com.yapp.growth.presentation.ui.main.home + +import com.yapp.growth.base.BaseViewModel +import com.yapp.growth.presentation.ui.main.home.HomeContract.* +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class HomeViewModel @Inject constructor( +) : BaseViewModel(HomeViewState()) { + + // TODO : 로그인 여부 체크(정호) + + override fun handleEvents(event: HomeEvent) { + when (event) { + is HomeEvent.OnCalendarDayClicked -> { + sendEffect({ HomeSideEffect.OpenBottomSheet }) + } + is HomeEvent.OnPlanClicked -> { + sendEffect({ HomeSideEffect.NavigateDetailPlanScreen }) + } + is HomeEvent.OnUserImageButtonClicked -> { + sendEffect({ HomeSideEffect.NavigateToInfoScreen }) + } + } + } +} diff --git a/presentation/src/main/java/com/yapp/growth/presentation/util/ModifierExt.kt b/presentation/src/main/java/com/yapp/growth/presentation/util/ModifierExt.kt new file mode 100644 index 00000000..825dfea7 --- /dev/null +++ b/presentation/src/main/java/com/yapp/growth/presentation/util/ModifierExt.kt @@ -0,0 +1,44 @@ +package com.yapp.growth.presentation.util + +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Paint +import androidx.compose.ui.graphics.drawscope.drawIntoCanvas +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +fun Modifier.advancedShadow( + color: Color = Color.Black, + alpha: Float = 0f, + cornersRadius: Dp = 0.dp, + shadowBlurRadius: Dp = 0.dp, + offsetY: Dp = 0.dp, + offsetX: Dp = 0.dp +) = drawBehind { + + val shadowColor = color.copy(alpha = alpha).toArgb() + val transparentColor = color.copy(alpha = 0f).toArgb() + + drawIntoCanvas { + val paint = Paint() + val frameworkPaint = paint.asFrameworkPaint() + frameworkPaint.color = transparentColor + frameworkPaint.setShadowLayer( + shadowBlurRadius.toPx(), + offsetX.toPx(), + offsetY.toPx(), + shadowColor + ) + it.drawRoundRect( + 0f, + 0f, + this.size.width, + this.size.height, + cornersRadius.toPx(), + cornersRadius.toPx(), + paint + ) + } +} \ No newline at end of file diff --git a/presentation/src/main/res/drawable/bg_calendar_ripple.xml b/presentation/src/main/res/drawable/bg_calendar_ripple.xml new file mode 100644 index 00000000..87734e89 --- /dev/null +++ b/presentation/src/main/res/drawable/bg_calendar_ripple.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/presentation/src/main/res/drawable/bg_calendar_selection.xml b/presentation/src/main/res/drawable/bg_calendar_selection.xml new file mode 100644 index 00000000..9e811530 --- /dev/null +++ b/presentation/src/main/res/drawable/bg_calendar_selection.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/presentation/src/main/res/drawable/bg_calendar_today.xml b/presentation/src/main/res/drawable/bg_calendar_today.xml new file mode 100644 index 00000000..6405d579 --- /dev/null +++ b/presentation/src/main/res/drawable/bg_calendar_today.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_arrow_box_left.xml b/presentation/src/main/res/drawable/ic_arrow_box_left.xml new file mode 100644 index 00000000..7dc914cb --- /dev/null +++ b/presentation/src/main/res/drawable/ic_arrow_box_left.xml @@ -0,0 +1,19 @@ + + + + diff --git a/presentation/src/main/res/drawable/ic_arrow_box_right.xml b/presentation/src/main/res/drawable/ic_arrow_box_right.xml new file mode 100644 index 00000000..c0d2a016 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_arrow_box_right.xml @@ -0,0 +1,19 @@ + + + + diff --git a/presentation/src/main/res/drawable/ic_arrow_left.xml b/presentation/src/main/res/drawable/ic_arrow_left.xml deleted file mode 100644 index b7dcd266..00000000 --- a/presentation/src/main/res/drawable/ic_arrow_left.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - diff --git a/presentation/src/main/res/drawable/ic_arrow_right.xml b/presentation/src/main/res/drawable/ic_arrow_right.xml deleted file mode 100644 index 62b354de..00000000 --- a/presentation/src/main/res/drawable/ic_arrow_right.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - diff --git a/presentation/src/main/res/drawable/ic_calendar.xml b/presentation/src/main/res/drawable/ic_calendar.xml index 7da49450..68257deb 100644 --- a/presentation/src/main/res/drawable/ic_calendar.xml +++ b/presentation/src/main/res/drawable/ic_calendar.xml @@ -1,34 +1,34 @@ + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + android:strokeColor="#1B1B1B"/> + android:strokeColor="#1B1B1B"/> + android:pathData="M9.166,15.858C8.916,15.858 8.708,15.631 8.708,15.356V13.088L8.592,13.228C8.42,13.436 8.133,13.449 7.944,13.268C7.754,13.081 7.742,12.766 7.907,12.559L8.824,11.441C8.952,11.288 9.154,11.234 9.331,11.307C9.509,11.381 9.625,11.568 9.625,11.776V15.356C9.625,15.637 9.423,15.858 9.166,15.858Z" + android:fillColor="#1B1B1B"/> + android:pathData="M12.375,15.858C11.362,15.858 10.542,15.035 10.542,14.018V13.115C10.542,12.098 11.362,11.275 12.375,11.275C13.388,11.275 14.208,12.098 14.208,13.115V14.018C14.208,15.035 13.388,15.858 12.375,15.858ZM12.375,12.285C11.915,12.285 11.542,12.66 11.542,13.122V14.025C11.542,14.487 11.915,14.861 12.375,14.861C12.835,14.861 13.208,14.487 13.208,14.025V13.122C13.208,12.66 12.835,12.285 12.375,12.285Z" + android:fillColor="#1B1B1B"/> diff --git a/presentation/src/main/res/drawable/ic_default_user_image.png b/presentation/src/main/res/drawable/ic_default_user_image.png new file mode 100644 index 00000000..628547ea Binary files /dev/null and b/presentation/src/main/res/drawable/ic_default_user_image.png differ diff --git a/presentation/src/main/res/drawable/ic_event_note.xml b/presentation/src/main/res/drawable/ic_event_note.xml deleted file mode 100644 index bbc8c3c4..00000000 --- a/presentation/src/main/res/drawable/ic_event_note.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - diff --git a/presentation/src/main/res/drawable/ic_have_not_plan.xml b/presentation/src/main/res/drawable/ic_have_not_plan.xml new file mode 100644 index 00000000..28923732 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_have_not_plan.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_list.xml b/presentation/src/main/res/drawable/ic_list.xml new file mode 100644 index 00000000..3f3d162b --- /dev/null +++ b/presentation/src/main/res/drawable/ic_list.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/presentation/src/main/res/drawable/ic_plan_etc.xml b/presentation/src/main/res/drawable/ic_plan_etc.xml new file mode 100644 index 00000000..c8246a78 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_plan_etc.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_plan_meal.xml b/presentation/src/main/res/drawable/ic_plan_meal.xml new file mode 100644 index 00000000..2e30df2c --- /dev/null +++ b/presentation/src/main/res/drawable/ic_plan_meal.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/presentation/src/main/res/drawable/ic_plan_meeting.xml b/presentation/src/main/res/drawable/ic_plan_meeting.xml new file mode 100644 index 00000000..2c94a005 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_plan_meeting.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_plan_trip.xml b/presentation/src/main/res/drawable/ic_plan_trip.xml new file mode 100644 index 00000000..8fd95692 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_plan_trip.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_transparent_arrow_bottom.xml b/presentation/src/main/res/drawable/ic_transparent_arrow_bottom.xml new file mode 100644 index 00000000..8e008037 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_transparent_arrow_bottom.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_transparent_arrow_right.xml b/presentation/src/main/res/drawable/ic_transparent_arrow_right.xml new file mode 100644 index 00000000..a316a378 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_transparent_arrow_right.xml @@ -0,0 +1,13 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_transparent_arrow_top.xml b/presentation/src/main/res/drawable/ic_transparent_arrow_top.xml new file mode 100644 index 00000000..ff1408a5 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_transparent_arrow_top.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 0f8e4138..76939395 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -11,6 +11,9 @@ 약속잡기 약속관리 + 오늘의 약속 + 회원가입하고, 편리한 약속잡기를 경험하세요! + 다음 약속 테마를 선택해 주세요!