diff --git a/Near/app/src/main/java/com/alarmy/near/data/mapper/FriendMapper.kt b/Near/app/src/main/java/com/alarmy/near/data/mapper/FriendMapper.kt index 4a2b19d9..fcfc70c5 100644 --- a/Near/app/src/main/java/com/alarmy/near/data/mapper/FriendMapper.kt +++ b/Near/app/src/main/java/com/alarmy/near/data/mapper/FriendMapper.kt @@ -1,16 +1,21 @@ package com.alarmy.near.data.mapper -import com.alarmy.near.model.Friend +import com.alarmy.near.model.ContactFrequency +import com.alarmy.near.model.FriendSummary import com.alarmy.near.network.response.FriendEntity -fun FriendEntity.toModel(): Friend = - Friend( - friendId = friendId, - position = position, - source = source, +fun FriendEntity.toModel(): FriendSummary = + FriendSummary( + id = friendId, name = name, - imageUrl = imageUrl, - fileName = fileName, - checkRate = checkRate, - lastContactAt = lastContactAt, - ) + profileImageUrl = imageUrl, + lastContactedAt = lastContactAt, + isContacted = true, + contactFrequency = + when (checkRate) { + in 0..29 -> ContactFrequency.LOW + in 30..69 -> ContactFrequency.MIDDLE + in 70..100 -> ContactFrequency.HIGH + else -> ContactFrequency.LOW + }, + ) diff --git a/Near/app/src/main/java/com/alarmy/near/data/mapper/MonthlyFriendMapper.kt b/Near/app/src/main/java/com/alarmy/near/data/mapper/MonthlyFriendMapper.kt new file mode 100644 index 00000000..d9ba2e49 --- /dev/null +++ b/Near/app/src/main/java/com/alarmy/near/data/mapper/MonthlyFriendMapper.kt @@ -0,0 +1,13 @@ +package com.alarmy.near.data.mapper + +import com.alarmy.near.model.monthly.MonthlyFriend +import com.alarmy.near.model.monthly.MonthlyFriendType +import com.alarmy.near.network.response.MonthlyFriendEntity + +fun MonthlyFriendEntity.toModel(): MonthlyFriend = + MonthlyFriend( + friendId = friendId, + name = name, + type = MonthlyFriendType.from(type), + nextContactAt = nextContactAt, + ) diff --git a/Near/app/src/main/java/com/alarmy/near/data/repository/DefaultFriendRepository.kt b/Near/app/src/main/java/com/alarmy/near/data/repository/DefaultFriendRepository.kt index dd155182..61ae63e7 100644 --- a/Near/app/src/main/java/com/alarmy/near/data/repository/DefaultFriendRepository.kt +++ b/Near/app/src/main/java/com/alarmy/near/data/repository/DefaultFriendRepository.kt @@ -1,7 +1,8 @@ package com.alarmy.near.data.repository import com.alarmy.near.data.mapper.toModel -import com.alarmy.near.model.Friend +import com.alarmy.near.model.FriendSummary +import com.alarmy.near.model.monthly.MonthlyFriend import com.alarmy.near.network.service.FriendService import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -12,7 +13,7 @@ class DefaultFriendRepository constructor( private val friendService: FriendService, ) : FriendRepository { - override fun fetchFriends(): Flow> = + override fun fetchFriends(): Flow> = flow { emit( friendService.fetchFriends().map { @@ -20,4 +21,13 @@ class DefaultFriendRepository }, ) } + + override fun fetchMonthlyFriends(): Flow> = + flow { + emit( + friendService.fetchMonthlyFriends().map { + it.toModel() + }, + ) + } } diff --git a/Near/app/src/main/java/com/alarmy/near/data/repository/FriendRepository.kt b/Near/app/src/main/java/com/alarmy/near/data/repository/FriendRepository.kt index b7e76baa..425654fe 100644 --- a/Near/app/src/main/java/com/alarmy/near/data/repository/FriendRepository.kt +++ b/Near/app/src/main/java/com/alarmy/near/data/repository/FriendRepository.kt @@ -1,8 +1,11 @@ package com.alarmy.near.data.repository -import com.alarmy.near.model.Friend +import com.alarmy.near.model.FriendSummary +import com.alarmy.near.model.monthly.MonthlyFriend import kotlinx.coroutines.flow.Flow interface FriendRepository { - fun fetchFriends(): Flow> + fun fetchFriends(): Flow> + + fun fetchMonthlyFriends(): Flow> } diff --git a/Near/app/src/main/java/com/alarmy/near/model/ContactSummary.kt b/Near/app/src/main/java/com/alarmy/near/model/ContactSummary.kt deleted file mode 100644 index 2a98ee89..00000000 --- a/Near/app/src/main/java/com/alarmy/near/model/ContactSummary.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.alarmy.near.model - -import androidx.compose.runtime.Immutable -import java.time.LocalDate -import java.time.format.DateTimeFormatter - -@Immutable -data class ContactSummary( - val id: String, - val name: String, - val profileImageUrl: String, - val lastContactedAt: LocalDate, - val isContacted: Boolean, - val contactFrequency: ContactFrequency, -) { - val formattedDate: String - get() = lastContactedAt.format(formatter) - - companion object { - private val formatter = DateTimeFormatter.ofPattern("MMM d, yyyy") - } -} diff --git a/Near/app/src/main/java/com/alarmy/near/model/Friend.kt b/Near/app/src/main/java/com/alarmy/near/model/Friend.kt deleted file mode 100644 index ebd71a9b..00000000 --- a/Near/app/src/main/java/com/alarmy/near/model/Friend.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.alarmy.near.model - -data class Friend( - val friendId: String, - val position: Int, - val source: String, - val name: String, - val imageUrl: String? = null, - val fileName: String? = null, - val checkRate: Int, - val lastContactAt: String? = null, -) diff --git a/Near/app/src/main/java/com/alarmy/near/model/FriendSummary.kt b/Near/app/src/main/java/com/alarmy/near/model/FriendSummary.kt new file mode 100644 index 00000000..49249003 --- /dev/null +++ b/Near/app/src/main/java/com/alarmy/near/model/FriendSummary.kt @@ -0,0 +1,13 @@ +package com.alarmy.near.model + +import androidx.compose.runtime.Immutable + +@Immutable +data class FriendSummary( + val id: String, + val name: String, + val profileImageUrl: String?, + val lastContactedAt: String?, + val isContacted: Boolean, + val contactFrequency: ContactFrequency, +) diff --git a/Near/app/src/main/java/com/alarmy/near/model/MonthlyContact.kt b/Near/app/src/main/java/com/alarmy/near/model/monthly/MonthlyFriend.kt similarity index 89% rename from Near/app/src/main/java/com/alarmy/near/model/MonthlyContact.kt rename to Near/app/src/main/java/com/alarmy/near/model/monthly/MonthlyFriend.kt index 286a7da4..ac4fbb1b 100644 --- a/Near/app/src/main/java/com/alarmy/near/model/MonthlyContact.kt +++ b/Near/app/src/main/java/com/alarmy/near/model/monthly/MonthlyFriend.kt @@ -1,13 +1,13 @@ -package com.alarmy.near.model +package com.alarmy.near.model.monthly import java.time.LocalDate import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit -data class MonthlyContact( +data class MonthlyFriend( val friendId: String, val name: String, - val type: String, + val type: MonthlyFriendType, val nextContactAt: String, ) { fun daysUntilNextContact(today: LocalDate): String { diff --git a/Near/app/src/main/java/com/alarmy/near/model/monthly/MonthlyFriendType.kt b/Near/app/src/main/java/com/alarmy/near/model/monthly/MonthlyFriendType.kt new file mode 100644 index 00000000..553d6939 --- /dev/null +++ b/Near/app/src/main/java/com/alarmy/near/model/monthly/MonthlyFriendType.kt @@ -0,0 +1,22 @@ +package com.alarmy.near.model.monthly + +import androidx.annotation.DrawableRes +import com.alarmy.near.R + +enum class MonthlyFriendType( + @param:DrawableRes val imageSrc: Int, +) { + ANNIVERSARY(R.drawable.icon_visual_24_heart), + BIRTHDAY(R.drawable.icon_visual_cake), + MESSAGE(R.drawable.icon_visual_mail), + ; + + companion object { + private const val ERROR_MESSAGE_NOT_FOUND_MONTHLY_TYPE = "일치하는 타입이 없습니다" + + fun from(value: String): MonthlyFriendType = + runCatching { valueOf(value.uppercase()) }.getOrNull() ?: throw IllegalStateException( + ERROR_MESSAGE_NOT_FOUND_MONTHLY_TYPE, + ) + } +} diff --git a/Near/app/src/main/java/com/alarmy/near/network/response/MonthlyFriendEntity.kt b/Near/app/src/main/java/com/alarmy/near/network/response/MonthlyFriendEntity.kt new file mode 100644 index 00000000..e60feaff --- /dev/null +++ b/Near/app/src/main/java/com/alarmy/near/network/response/MonthlyFriendEntity.kt @@ -0,0 +1,11 @@ +package com.alarmy.near.network.response + +import kotlinx.serialization.Serializable + +@Serializable +data class MonthlyFriendEntity( + val friendId: String, + val name: String, + val type: String, + val nextContactAt: String, +) diff --git a/Near/app/src/main/java/com/alarmy/near/network/service/FriendService.kt b/Near/app/src/main/java/com/alarmy/near/network/service/FriendService.kt index a412dadc..0beca5dc 100644 --- a/Near/app/src/main/java/com/alarmy/near/network/service/FriendService.kt +++ b/Near/app/src/main/java/com/alarmy/near/network/service/FriendService.kt @@ -1,9 +1,13 @@ package com.alarmy.near.network.service import com.alarmy.near.network.response.FriendEntity +import com.alarmy.near.network.response.MonthlyFriendEntity import retrofit2.http.GET interface FriendService { @GET("/friend/list") suspend fun fetchFriends(): List + + @GET("/friend/monthly") + suspend fun fetchMonthlyFriends(): List } diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/friendprofile/navigation/FriendProfileNavigation.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/friendprofile/navigation/FriendProfileNavigation.kt index 04100225..c3b4608a 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/friendprofile/navigation/FriendProfileNavigation.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/friendprofile/navigation/FriendProfileNavigation.kt @@ -8,10 +8,15 @@ import com.alarmy.near.presentation.feature.friendprofile.FriendProfileRoute import kotlinx.serialization.Serializable @Serializable -object RouteFriendProfile +data class RouteFriendProfile( + val friendId: String, +) -fun NavController.navigateToFriendProfile(navOptions: NavOptions) { - navigate(RouteFriendProfile, navOptions) +fun NavController.navigateToFriendProfile( + friendId: String, + navOptions: NavOptions? = null, +) { + navigate(RouteFriendProfile(friendId), navOptions) } fun NavGraphBuilder.friendProfileNavGraph( diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeScreen.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeScreen.kt index 56fa7f29..61612787 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeScreen.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeScreen.kt @@ -22,11 +22,15 @@ import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -49,12 +53,14 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.alarmy.near.R import com.alarmy.near.model.ContactFrequency -import com.alarmy.near.model.ContactSummary -import com.alarmy.near.model.MonthlyContact +import com.alarmy.near.model.FriendSummary +import com.alarmy.near.model.monthly.MonthlyFriend +import com.alarmy.near.model.monthly.MonthlyFriendType import com.alarmy.near.presentation.feature.home.component.MyContacts import com.alarmy.near.presentation.ui.extension.dropShadow import com.alarmy.near.presentation.ui.extension.onNoRippleClick import com.alarmy.near.presentation.ui.theme.NearTheme +import kotlinx.coroutines.launch import java.time.LocalDate private const val MINIMUM_PAGE_COUNT_TO_SHOW_UI = 2 @@ -66,24 +72,24 @@ internal fun HomeRoute( onContactClick: (String) -> Unit = {}, onAlarmClick: () -> Unit = {}, onMyPageClick: () -> Unit = {}, + onAddContactClick: () -> Unit = {}, ) { - val uiState = viewModel.friendsFlow.collectAsStateWithLifecycle() + LaunchedEffect(Unit) { + launch { + viewModel.errorEvent.collect { + onShowErrorSnackBar(it) + } + } + } + val friends = viewModel.friendsFlow.collectAsStateWithLifecycle() + val monthlyFriends = viewModel.monthlyFriendFlow.collectAsStateWithLifecycle() HomeScreen( - onContactClick = {}, - onAlarmClick = {}, - onMyPageClick = {}, - contacts = - List(6) { - ContactSummary( - id = "2003", - name = "일이삼사오육칠팔구", - profileImageUrl = "https://search.yahoo.com/search?p=partiendo", - lastContactedAt = LocalDate.of(2025, 7, 25), - isContacted = false, - contactFrequency = ContactFrequency.LOW, - ) - }, - monthlyContacts = emptyList(), + onContactClick = onContactClick, + onAlarmClick = onAlarmClick, + onMyPageClick = onMyPageClick, + onAddContactClick = onAddContactClick, + contacts = friends.value, + monthlyFriends = monthlyFriends.value, ) } @@ -94,8 +100,9 @@ internal fun HomeScreen( onContactClick: (String) -> Unit = { _ -> }, onMyPageClick: () -> Unit = {}, onAlarmClick: () -> Unit = {}, - contacts: List, - monthlyContacts: List, + onAddContactClick: () -> Unit = {}, + contacts: List, + monthlyFriends: List, ) { val density = LocalDensity.current val statusBarHeightDp = with(density) { WindowInsets.statusBars.getTop(density).toDp() } @@ -107,6 +114,7 @@ internal fun HomeScreen( contactsWithPage.count() + if (contactsWithPage.lastOrNull()?.count() == 5) 1 else 0 }, ) + val dropdownState = remember { mutableStateOf(false) } Surface(modifier = modifier) { Column( @@ -169,7 +177,7 @@ internal fun HomeScreen( color = NearTheme.colors.WHITE_FFFFFF, ) Spacer(modifier = Modifier.height(16.dp)) - if (monthlyContacts.isEmpty()) { + if (monthlyFriends.isEmpty()) { Surface( modifier = Modifier @@ -201,12 +209,12 @@ internal fun HomeScreen( horizontalArrangement = Arrangement.spacedBy(12.dp), ) { items( - count = monthlyContacts.size, + count = monthlyFriends.size, key = { - monthlyContacts[it].friendId + monthlyFriends[it].friendId }, ) { - val monthlyContact = monthlyContacts[it] + val monthlyContact = monthlyFriends[it] val now = LocalDate.now() Surface( modifier.dropShadow( @@ -227,7 +235,7 @@ internal fun HomeScreen( verticalAlignment = Alignment.CenterVertically, ) { Image( - painterResource(R.drawable.icon_visual_mail), + painterResource(monthlyContact.type.imageSrc), contentDescription = "", ) Spacer(modifier = Modifier.width(8.dp)) @@ -261,6 +269,7 @@ internal fun HomeScreen( } Spacer(modifier = Modifier.height(24.dp)) + Box( modifier = Modifier @@ -270,6 +279,15 @@ internal fun HomeScreen( shape = RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp), ), ) { + MyContacts( + modifier = Modifier.align(Alignment.TopCenter), + contactsWithPage = contactsWithPage, + pagerState = pagerState, + onContactClick = onContactClick, + onAddContactClick = { + onAddContactClick() + }, + ) Row( modifier = Modifier @@ -284,21 +302,37 @@ internal fun HomeScreen( style = NearTheme.typography.H2_18_BOLD, color = NearTheme.colors.BLACK_1A1A1A, ) - Icon( - painterResource(R.drawable.ic_32_menu), - contentDescription = stringResource(R.string.home_my_people_setting), - ) + Column { + Image( + modifier = + Modifier.onNoRippleClick(onClick = { + dropdownState.value = true + }), + painter = painterResource(R.drawable.ic_32_menu), + contentDescription = stringResource(R.string.home_my_people_setting), + ) + DropdownMenu( + modifier = Modifier.background(color = NearTheme.colors.WHITE_FFFFFF), + expanded = dropdownState.value, + shape = RoundedCornerShape(12.dp), + onDismissRequest = { dropdownState.value = false }, + ) { + DropdownMenuItem( + onClick = { + // TODO 연락처 화면 이동 + dropdownState.value = false + }, + text = { + Text( + stringResource(R.string.home_menu_text_add_friend), + style = NearTheme.typography.B2_14_MEDIUM, + color = NearTheme.colors.BLACK_1A1A1A, + ) + }, + ) + } + } } - Spacer(modifier = Modifier.height(16.dp)) - MyContacts( - modifier = Modifier.align(Alignment.TopCenter), - contactsWithPage = contactsWithPage, - pagerState = pagerState, - onContactClick = onContactClick, - onAddContactClick = { - // TODO Contact 클릭 이벤트 구현 - }, - ) if (contactsWithPage.size >= MINIMUM_PAGE_COUNT_TO_SHOW_UI) { Column(modifier = Modifier.align(Alignment.BottomCenter)) { @@ -348,21 +382,21 @@ internal fun HomeScreenPreview() { onContactClick = {}, contacts = List(6) { - ContactSummary( + FriendSummary( id = "2003", name = "일이삼사오육칠팔구", profileImageUrl = "https://search.yahoo.com/search?p=partiendo", - lastContactedAt = LocalDate.of(2025, 7, 25), + lastContactedAt = "2025-07-16", isContacted = false, contactFrequency = ContactFrequency.HIGH, ) }, - monthlyContacts = + monthlyFriends = List(4) { - MonthlyContact( + MonthlyFriend( friendId = "intellegat$it", name = "Stacey Stewart", - type = "ANNIVERSARY", + type = MonthlyFriendType.ANNIVERSARY, nextContactAt = "2025-09-30", ) }, diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeViewModel.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeViewModel.kt index aba76bc8..899c1503 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeViewModel.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/HomeViewModel.kt @@ -3,13 +3,14 @@ package com.alarmy.near.presentation.feature.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.alarmy.near.data.repository.FriendRepository -import com.alarmy.near.model.Friend -import com.alarmy.near.presentation.feature.home.model.HomeUiState +import com.alarmy.near.model.FriendSummary +import com.alarmy.near.model.monthly.MonthlyFriend import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import javax.inject.Inject @@ -17,22 +18,33 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( - private val friendRepository: FriendRepository, + friendRepository: FriendRepository, ) : ViewModel() { - // Example: 여러번 초기화되는 StateFlow - private val _uiStateFlow = MutableStateFlow(HomeUiState.Loading) - val uiStateFlow = _uiStateFlow.asStateFlow() + private val _errorEvent = Channel() + val errorEvent = _errorEvent.receiveAsFlow() - val friendsFlow: StateFlow> = + val friendsFlow: StateFlow> = friendRepository .fetchFriends() + .catch { + _errorEvent.send(it) + } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = emptyList(), ) - fun removeContact(id: Long) { - // contactRepository.removeContact(id) - } + val monthlyFriendFlow: + StateFlow> = + friendRepository + .fetchMonthlyFriends() + .catch { + _errorEvent.send(it) + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = emptyList(), + ) } diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/ContactItem.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/ContactItem.kt index d9914ca0..15fd3d28 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/ContactItem.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/ContactItem.kt @@ -20,23 +20,22 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.alarmy.near.R import com.alarmy.near.model.ContactFrequency -import com.alarmy.near.model.ContactSummary +import com.alarmy.near.model.FriendSummary import com.alarmy.near.presentation.ui.extension.onNoRippleClick import com.alarmy.near.presentation.ui.theme.NearTheme -import java.time.LocalDate private const val MAX_WIDTH_OF_NAME_TEXT = 97 @Composable fun ContactItem( modifier: Modifier = Modifier, - contactSummary: ContactSummary, + friendSummary: FriendSummary, onClick: (contactId: String) -> Unit = {}, ) { Column( modifier = modifier.onNoRippleClick { - onClick(contactSummary.id) + onClick(friendSummary.id) }, horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -51,7 +50,7 @@ fun ContactItem( .align(Alignment.TopEnd) .offset(x = 4.dp, y = (-4).dp), painter = - when (contactSummary.contactFrequency) { + when (friendSummary.contactFrequency) { ContactFrequency.LOW -> painterResource(R.drawable.ic_visual_24_emoji_0) ContactFrequency.MIDDLE -> painterResource(R.drawable.ic_visual_24_emoji_50) ContactFrequency.HIGH -> painterResource(R.drawable.ic_visual_24_emoji_100) @@ -62,7 +61,7 @@ fun ContactItem( Spacer(modifier = Modifier.height(6.dp)) Text( modifier = Modifier.width(MAX_WIDTH_OF_NAME_TEXT.dp), - text = contactSummary.name, + text = friendSummary.name, style = NearTheme.typography.B2_14_BOLD, textAlign = TextAlign.Center, overflow = TextOverflow.Ellipsis, @@ -70,33 +69,35 @@ fun ContactItem( color = NearTheme.colors.BLACK_1A1A1A, ) Spacer(modifier = Modifier.height(1.dp)) - Row { + Row(verticalAlignment = Alignment.CenterVertically) { Text( - contactSummary.formattedDate, + friendSummary.lastContactedAt ?: "", style = NearTheme.typography.FC_12_MEDIUM, textAlign = TextAlign.Center, color = NearTheme.colors.GRAY02_B7B7B7, ) Spacer(modifier = Modifier.width(2.dp)) - Image( - painter = painterResource(R.drawable.ic_12_check), - contentDescription = "", - ) + if (friendSummary.lastContactedAt != null) { + Image( + painter = painterResource(R.drawable.ic_12_check), + contentDescription = "", + ) + } } } } @Preview(showBackground = true) @Composable -fun ContactItemPreview_Default() { +fun ContactItemPreview() { ContactItem( modifier = Modifier.padding(top = 10.dp), - contactSummary = - ContactSummary( + friendSummary = + FriendSummary( id = "123L", name = "홍길동", profileImageUrl = "", - lastContactedAt = LocalDate.of(2025, 5, 31), + lastContactedAt = "2025-04-21", isContacted = true, contactFrequency = ContactFrequency.HIGH, ), diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/MyContacts.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/MyContacts.kt index 526373b3..c09f3796 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/MyContacts.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/component/MyContacts.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.alarmy.near.model.ContactFrequency -import com.alarmy.near.model.ContactSummary +import com.alarmy.near.model.FriendSummary import com.alarmy.near.presentation.ui.theme.NearTheme import java.time.LocalDate @@ -27,7 +27,7 @@ private const val OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT = 34 @Composable fun MyContacts( modifier: Modifier = Modifier, - contactsWithPage: List>, + contactsWithPage: List>, pagerState: PagerState = rememberPagerState( initialPage = 0, @@ -76,7 +76,7 @@ fun MyContacts( horizontalArrangement = Arrangement.Center, ) { ContactItem( - contactSummary = contactsWithPage[page][0], + friendSummary = contactsWithPage[page][0], onClick = onContactClick, ) Spacer(modifier = Modifier.width((60 - OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT).dp)) @@ -94,13 +94,13 @@ fun MyContacts( ) { Spacer(modifier = Modifier.height(112.dp)) ContactItem( - contactSummary = contactsWithPage[page][0], + friendSummary = contactsWithPage[page][0], onClick = onContactClick, ) Spacer(modifier = Modifier.height(16.dp)) Row { ContactItem( - contactSummary = contactsWithPage[page][1], + friendSummary = contactsWithPage[page][1], onClick = onContactClick, ) Spacer(modifier = Modifier.width((118 - OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT).dp)) @@ -120,17 +120,17 @@ fun MyContacts( Box { ContactItem( modifier = Modifier.align(Alignment.TopCenter), - contactSummary = contactsWithPage[page][0], + friendSummary = contactsWithPage[page][0], onClick = onContactClick, ) Row(modifier = Modifier.padding(top = 92.dp, bottom = 78.dp)) { ContactItem( - contactSummary = contactsWithPage[page][1], + friendSummary = contactsWithPage[page][1], onClick = onContactClick, ) Spacer(modifier = Modifier.width((141 - OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT).dp)) ContactItem( - contactSummary = contactsWithPage[page][2], + friendSummary = contactsWithPage[page][2], onClick = onContactClick, ) } @@ -151,17 +151,17 @@ fun MyContacts( Box { ContactItem( modifier = Modifier.align(Alignment.TopCenter), - contactSummary = contactsWithPage[page][0], + friendSummary = contactsWithPage[page][0], onClick = onContactClick, ) Row(modifier = Modifier.padding(top = 62.dp)) { ContactItem( - contactSummary = contactsWithPage[page][1], + friendSummary = contactsWithPage[page][1], onClick = onContactClick, ) Spacer(modifier = Modifier.width((138 - OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT).dp)) ContactItem( - contactSummary = contactsWithPage[page][2], + friendSummary = contactsWithPage[page][2], onClick = onContactClick, ) } @@ -169,7 +169,7 @@ fun MyContacts( Spacer(modifier = Modifier.height(16.dp)) Row { ContactItem( - contactSummary = contactsWithPage[page][3], + friendSummary = contactsWithPage[page][3], onClick = onContactClick, ) Spacer(modifier = Modifier.width((51 - OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT).dp)) @@ -189,17 +189,17 @@ fun MyContacts( Box { ContactItem( modifier = Modifier.align(Alignment.TopCenter), - contactSummary = contactsWithPage[page][0], + friendSummary = contactsWithPage[page][0], onClick = onContactClick, ) Row(modifier = Modifier.padding(top = 62.dp)) { ContactItem( - contactSummary = contactsWithPage[page][1], + friendSummary = contactsWithPage[page][1], onClick = onContactClick, ) Spacer(modifier = Modifier.width((138 - OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT).dp)) ContactItem( - contactSummary = contactsWithPage[page][2], + friendSummary = contactsWithPage[page][2], onClick = onContactClick, ) } @@ -207,12 +207,12 @@ fun MyContacts( Spacer(modifier = Modifier.height(16.dp)) Row { ContactItem( - contactSummary = contactsWithPage[page][3], + friendSummary = contactsWithPage[page][3], onClick = onContactClick, ) Spacer(modifier = Modifier.width((51 - OVERFLOW_WIDTH_OF_CONTACT_ITEM_BY_NAME_TEXT).dp)) ContactItem( - contactSummary = contactsWithPage[page][4], + friendSummary = contactsWithPage[page][4], onClick = onContactClick, ) } @@ -234,11 +234,11 @@ fun MyContactsPreview() { modifier = Modifier.align(Alignment.Center), contactsWithPage = List(5) { - ContactSummary( + FriendSummary( id = "2003", name = "일이삼사오육칠팔구", profileImageUrl = "https://search.yahoo.com/search?p=partiendo", - lastContactedAt = LocalDate.of(2025, 7, 25), + lastContactedAt = "2025-04-21", isContacted = false, contactFrequency = ContactFrequency.LOW, ) diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/navigation/HomeNavigation.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/navigation/HomeNavigation.kt index 4569fd79..804ed944 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/navigation/HomeNavigation.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/home/navigation/HomeNavigation.kt @@ -22,6 +22,7 @@ fun NavGraphBuilder.homeNavGraph( onContactClick: (String) -> Unit = {}, onAlarmClick: () -> Unit = {}, onMyPageClick: () -> Unit = {}, + onAddContactClick: () -> Unit = {}, ) { composable { backStackEntry -> HomeRoute( @@ -29,6 +30,7 @@ fun NavGraphBuilder.homeNavGraph( onContactClick = onContactClick, onAlarmClick = onAlarmClick, onMyPageClick = onMyPageClick, + onAddContactClick = onAddContactClick, ) } } diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt index 78d50a02..94f9e995 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import com.alarmy.near.presentation.feature.friendprofile.navigation.friendProfileNavGraph +import com.alarmy.near.presentation.feature.friendprofile.navigation.navigateToFriendProfile import com.alarmy.near.presentation.feature.friendprofileedittor.navigation.friendProfileEditorNavGraph import com.alarmy.near.presentation.feature.home.navigation.RouteHome import com.alarmy.near.presentation.feature.home.navigation.homeNavGraph @@ -32,10 +33,11 @@ internal fun NearNavHost( homeNavGraph( onShowErrorSnackBar = onShowSnackbar, onContactClick = { contactId -> - // 예시: navController.navigate(RouteContact(it)) + navController.navigateToFriendProfile(friendId = contactId) }, onMyPageClick = {}, onAlarmClick = {}, + onAddContactClick = {}, ) } } diff --git a/Near/app/src/main/res/values/strings.xml b/Near/app/src/main/res/values/strings.xml index 3d71e0ec..3606877d 100644 --- a/Near/app/src/main/res/values/strings.xml +++ b/Near/app/src/main/res/values/strings.xml @@ -37,4 +37,5 @@ 2주 매달 6개월 + 사람 추가