diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f6801c8b..f0b717d7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -41,20 +41,19 @@ dependencies { androidTestImplementation(libs.androidx.test.espresso) } -/*tasks.getByPath(":app:preBuild").dependsOn("makeFileExecutable") - -tasks.register("makeFileExecutable") { - commandLine("chmod", "+x", "${rootProject.rootDir}/.git/hooks/pre-commit") - dependsOn("installGitHook") -} +/*tasks.getByPath(":app:preBuild").dependsOn("installGitHook") tasks.register("installGitHook") { dependsOn("deletePreviousGitHook") from("${rootProject.rootDir}/script/pre-commit") into("${rootProject.rootDir}/.git/hooks") + eachFile { + fileMode = 777 + } } tasks.register("deletePreviousGitHook") { + val prePush = "${rootProject.rootDir}/.git/hooks/pre-commit" if (file(prePush).exists()) { delete(prePush) diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 81f669e0..324601a6 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -25,7 +25,7 @@ android:id="@+id/bnv_main_bottomNaviView" android:layout_width="match_parent" android:layout_height="70dp" - android:background="@color/gray_2" + android:background="@color/black_1" app:itemActiveIndicatorStyle="@style/Style.BottomNavigationView.Custom.Indicator" app:itemTextColor="@drawable/selector_text_color" app:layout_constraintBottom_toBottomOf="parent" diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index c83f7dc9..1234dd2b 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -7,6 +7,12 @@ java { targetCompatibility = JavaVersion.VERSION_17 } +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of("17")) + } +} + dependencies { compileOnly(libs.android.build) compileOnly(libs.kotlin.gradle) diff --git a/core/designsystem/src/main/java/com/wap/designsystem/Color.kt b/core/designsystem/src/main/java/com/wap/designsystem/Color.kt index b454f7e8..24496ec2 100644 --- a/core/designsystem/src/main/java/com/wap/designsystem/Color.kt +++ b/core/designsystem/src/main/java/com/wap/designsystem/Color.kt @@ -9,11 +9,13 @@ import androidx.compose.ui.graphics.Color val White = Color(0xFFFFFFFF) val Black = Color(0xFF000000) val BackgroundBlack = Color(0xFF131313) -val Black82 = Color(0xFF828282) // 3 -val Black42 = Color(0xFF424242) // 2 -val Black25 = Color(0xFF252424) // 1 -val GrayF4 = Color(0xFFF4F4F4) // 2 -val GrayA2 = Color(0xFFA2A2A2) // 1 +val Black82 = Color(0xFF828282) +val Black42 = Color(0xFF424242) +val Black25 = Color(0xFF252424) +val Gray82 = Color(0xFF828282) +val GrayBD = Color(0xFFBDBDBD) +val GrayF4 = Color(0xFFF4F4F4) +val GrayA2 = Color(0xFFA2A2A2) val Yellow = Color(0xFFFBCF34) @Stable @@ -26,6 +28,8 @@ class WappColor( black82: Color = Black82, grayA2: Color = GrayA2, grayF4: Color = GrayF4, + grayBD: Color = GrayBD, + gray82: Color = Gray82, yellow: Color = Yellow, ) { var white by mutableStateOf(white) @@ -34,15 +38,19 @@ class WappColor( private set var backgroundBlack by mutableStateOf(backgroundBlack) private set - var black25 by mutableStateOf(Black25) + var black25 by mutableStateOf(black25) private set - var black42 by mutableStateOf(Black42) + var black42 by mutableStateOf(black42) private set - var black82 by mutableStateOf(Black82) + var black82 by mutableStateOf(black82) private set - var grayA2 by mutableStateOf(GrayA2) + var grayA2 by mutableStateOf(grayA2) private set - var grayF4 by mutableStateOf(GrayF4) + var grayF4 by mutableStateOf(grayF4) + private set + var grayBD by mutableStateOf(grayBD) + private set + var gray82 by mutableStateOf(gray82) private set var yellow by mutableStateOf(yellow) private set diff --git a/core/domain/src/main/java/com/wap/wapp/core/domain/model/Notice.kt b/core/domain/src/main/java/com/wap/wapp/core/domain/model/Notice.kt new file mode 100644 index 00000000..45d2fda5 --- /dev/null +++ b/core/domain/src/main/java/com/wap/wapp/core/domain/model/Notice.kt @@ -0,0 +1,7 @@ +package com.wap.wapp.core.domain.model + +data class Notice( + val time: String, + val title: String, + val duration: String, +) diff --git a/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpScreen.kt b/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpScreen.kt index a4660a5b..46c7695d 100644 --- a/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpScreen.kt +++ b/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpScreen.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel @@ -172,12 +171,3 @@ internal fun SignUpScreen( } } } - -@Preview -@Composable -fun previewSignUpScreen() { - SignUpScreen( - navigateToSignIn = { }, - navigateToNotice = { }, - ) -} diff --git a/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpViewModel.kt b/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpViewModel.kt index e067b7ec..d060c859 100644 --- a/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpViewModel.kt +++ b/feature/auth/src/main/java/com/wap/wapp/feature/auth/signup/SignUpViewModel.kt @@ -4,12 +4,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.wap.wapp.core.domain.usecase.user.PostUserProfileUseCase import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch +import javax.inject.Inject @HiltViewModel class SignUpViewModel @Inject constructor( @@ -61,12 +61,12 @@ class SignUpViewModel @Inject constructor( _signUpSemester.value = semester } - companion object { - const val FIRST_SEMESTER = "1학기" - } - sealed class SignUpEvent { data object Success : SignUpEvent() data class Failure(val throwable: Throwable) : SignUpEvent() } + + companion object { + const val FIRST_SEMESTER = "1학기" + } } diff --git a/feature/notice/build.gradle.kts b/feature/notice/build.gradle.kts index 1d3de406..425af3a2 100644 --- a/feature/notice/build.gradle.kts +++ b/feature/notice/build.gradle.kts @@ -24,6 +24,7 @@ android { dependencies { implementation(project(":core:designresource")) implementation(project(":core:designsystem")) + implementation(project(":core:domain")) implementation(libs.bundles.androidx) implementation(libs.material) diff --git a/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeFragment.kt b/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeFragment.kt index cbf3f8cd..1ab3bcfc 100644 --- a/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeFragment.kt +++ b/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeFragment.kt @@ -4,57 +4,32 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment +import com.wap.designsystem.WappTheme +import dagger.hilt.android.AndroidEntryPoint -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" - -/** - * A simple [Fragment] subclass. - * Use the [NoticeFragment.newInstance] factory method to - * create an instance of this fragment. - */ +@AndroidEntryPoint class NoticeFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) - } - } + private lateinit var composeView: ComposeView override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_notice, container, false) + return ComposeView(requireContext()).also { + composeView = it + } } - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment NoticeFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - NoticeFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + composeView.setContent { + WappTheme { + NoticeScreen() } + } } } diff --git a/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeScreen.kt b/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeScreen.kt new file mode 100644 index 00000000..88f01857 --- /dev/null +++ b/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeScreen.kt @@ -0,0 +1,252 @@ +package com.wap.wapp.feature.notice + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +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.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.BottomSheetScaffold +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SheetState +import androidx.compose.material3.SheetValue +import androidx.compose.material3.Text +import androidx.compose.material3.rememberBottomSheetScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.layout +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.wap.designsystem.WappTheme +import com.wap.wapp.core.domain.model.Notice +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun NoticeScreen( + viewModel: NoticeViewModel = hiltViewModel(), +) { + var defaultHeight: Dp by remember { mutableStateOf(0.dp) } + var expandableHeight: Dp by remember { mutableStateOf(0.dp) } + val coroutineScope = rememberCoroutineScope() + val scaffoldState = rememberBottomSheetScaffoldState( + bottomSheetState = SheetState( + skipPartiallyExpanded = false, + initialValue = SheetValue.PartiallyExpanded, + skipHiddenState = true, + ), + ) + + Column( + modifier = Modifier + .fillMaxSize() + .background(WappTheme.colors.black25), + ) { + BottomSheetScaffold( + scaffoldState = scaffoldState, + sheetContainerColor = WappTheme.colors.black25, + sheetPeekHeight = defaultHeight, + sheetContent = { BottomSheetContent(expandableHeight) }, + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(WappTheme.colors.black25) + .layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + + defaultHeight = (constraints.maxHeight - placeable.height).toDp() + layout(placeable.width, placeable.height) { + placeable.placeRelative(0, 0) + } + }, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + expandableHeight = + constraints.maxHeight.toDp() - (placeable.height.toDp() * 2) + layout(placeable.width, placeable.height) { + placeable.placeRelative(0, 0) + } + } + .padding(vertical = 10.dp) + .fillMaxWidth(), + ) { + Image( + painter = painterResource(id = R.drawable.ic_threelines), + contentDescription = + stringResource(R.string.calendarToggleImageContextDescription), + modifier = Modifier + .clickable { + handleSheetState(coroutineScope, scaffoldState.bottomSheetState) + } + .padding(start = 16.dp), + ) + Text( + text = "2023. 10", + style = WappTheme.typography.titleBold, + color = WappTheme.colors.white, + modifier = Modifier.padding(start = 10.dp), + ) + } + + Image( + painter = painterResource(id = R.drawable.ic_calendar), + contentDescription = stringResource(id = R.string.calendarContextDescription), + contentScale = ContentScale.FillWidth, + modifier = Modifier + .padding(top = 10.dp) + .fillMaxWidth(), + ) + } + } + } +} + +@Composable +private fun BottomSheetContent(expandableHeight: Dp) { + Column( + horizontalAlignment = Alignment.Start, + modifier = Modifier.height(expandableHeight), + ) { + Text( + text = "10.25 수요일", + style = WappTheme.typography.titleBold, + color = WappTheme.colors.white, + modifier = Modifier.padding(start = 15.dp, bottom = 15.dp), + ) + NoticeList(getDummyNotices()) + } +} + +@Composable +private fun NoticeList(notices: List) { + LazyColumn( + contentPadding = PaddingValues(horizontal = 15.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.fillMaxWidth(), + ) { + itemsIndexed( + items = notices, + key = { _, notice -> notice.title }, + ) { _, notice -> + NoticeItem(notice = notice) + } + } +} + +@Composable +private fun NoticeItem(notice: Notice) { + Column { + Row( + modifier = Modifier + .background(WappTheme.colors.black25) + .fillMaxWidth() + .padding(top = 5.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = notice.time, + style = WappTheme.typography.contentBold, + color = WappTheme.colors.white, + ) + Box( + modifier = Modifier + .padding(10.dp) + .size(width = 4.dp, height = 20.dp) + .clip(RoundedCornerShape(10.dp)) + .background(WappTheme.colors.yellow), + ) + Column( + horizontalAlignment = Alignment.Start, + modifier = Modifier.padding(start = 12.dp), + ) { + Text( + text = notice.title, + style = WappTheme.typography.contentRegular, + color = WappTheme.colors.white, + ) + + Text( + text = notice.duration, + style = WappTheme.typography.captionRegular, + color = WappTheme.colors.grayBD, + ) + } + } + Divider( + color = WappTheme.colors.gray82, + thickness = (0.5).dp, + modifier = Modifier.padding(top = 15.dp), + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +private fun handleSheetState( + coroutineScope: CoroutineScope, + sheetState: SheetState, +) { + coroutineScope.launch { + when (sheetState.currentValue) { + SheetValue.Expanded -> sheetState.partialExpand() + SheetValue.PartiallyExpanded -> sheetState.expand() + SheetValue.Hidden -> sheetState.expand() + } + } +} + +// forTest +private fun getDummyNotices(): List = listOf( + Notice( + time = "19:00", + title = "동아리 MT", + duration = "19:00 ~ 21:00", + ), + Notice( + time = "19:00", + title = "동아리 MT2", + duration = "19:00 ~ 21:00", + ), + Notice( + time = "19:00", + title = "동아리 MT3", + duration = "19:00 ~ 21:00", + ), + Notice( + time = "19:00", + title = "동아리 MT4", + duration = "19:00 ~ 21:00", + ), + Notice( + time = "19:00", + title = "동아리 MT5", + duration = "19:00 ~ 21:00", + ), +) diff --git a/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeViewModel.kt b/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeViewModel.kt new file mode 100644 index 00000000..9364feb4 --- /dev/null +++ b/feature/notice/src/main/java/com/wap/wapp/feature/notice/NoticeViewModel.kt @@ -0,0 +1,10 @@ +package com.wap.wapp.feature.notice + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class NoticeViewModel @Inject constructor() : ViewModel() { + // Todo +} diff --git a/feature/notice/src/main/res/drawable/ic_calendar.png b/feature/notice/src/main/res/drawable/ic_calendar.png new file mode 100644 index 00000000..d7de95ee Binary files /dev/null and b/feature/notice/src/main/res/drawable/ic_calendar.png differ diff --git a/feature/notice/src/main/res/drawable/ic_threelines.xml b/feature/notice/src/main/res/drawable/ic_threelines.xml new file mode 100644 index 00000000..e7dc08ab --- /dev/null +++ b/feature/notice/src/main/res/drawable/ic_threelines.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/feature/notice/src/main/res/values/strings.xml b/feature/notice/src/main/res/values/strings.xml index 6048840e..b80baa46 100644 --- a/feature/notice/src/main/res/values/strings.xml +++ b/feature/notice/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - - Hello blank fragment - \ No newline at end of file + 공지사항 목록만을 보여주거나, 캘린더를 다시 보여줍니다. + 공지사항을 보여주는 달력입니다. +