diff --git a/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/mapper/MeetingResponseMapper.kt b/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/mapper/MeetingResponseMapper.kt index 373303171..3da4b380b 100644 --- a/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/mapper/MeetingResponseMapper.kt +++ b/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/mapper/MeetingResponseMapper.kt @@ -11,19 +11,22 @@ fun MeetingResponse.toMeeting(): Meeting = Meeting( id = id, name = name, - targetPosition = targetAddress, - meetingDate = date.parseToLocalDate(), - meetingTime = time.parseToLocalTime(), + date = date.toLocalDate(), + time = time.toLocalTime(), + destinationAddress = targetAddress, + departureAddress = departureAddress, + departureTime = departureTime.toLocalTime(), + routeTime = routeTime, mates = mates.map { Mate(nickname = it.nickname, imageUrl = it.imageUrl) }, inviteCode = inviteCode, ) -private fun String.parseToLocalDate(): LocalDate { +private fun String.toLocalDate(): LocalDate { val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") return LocalDate.parse(this, formatter) } -private fun String.parseToLocalTime(): LocalTime { +private fun String.toLocalTime(): LocalTime { val formatter = DateTimeFormatter.ofPattern("HH:mm") return LocalTime.parse(this, formatter) } diff --git a/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/response/MeetingResponse.kt b/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/response/MeetingResponse.kt index 7de38016e..b8d5ff19a 100644 --- a/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/response/MeetingResponse.kt +++ b/android/app/src/main/java/com/mulberry/ody/data/remote/core/entity/meeting/response/MeetingResponse.kt @@ -13,6 +13,12 @@ data class MeetingResponse( val date: String, @Json(name = "time") val time: String, + @Json(name = "departureAddress") + val departureAddress: String, + @Json(name = "departureTime") + val departureTime: String, + @Json(name = "routeTime") + val routeTime: Int, @Json(name = "targetAddress") val targetAddress: String, @Json(name = "targetLatitude") diff --git a/android/app/src/main/java/com/mulberry/ody/data/remote/core/repository/DefaultMeetingRepository.kt b/android/app/src/main/java/com/mulberry/ody/data/remote/core/repository/DefaultMeetingRepository.kt index be34cf16b..ccb5ea099 100644 --- a/android/app/src/main/java/com/mulberry/ody/data/remote/core/repository/DefaultMeetingRepository.kt +++ b/android/app/src/main/java/com/mulberry/ody/data/remote/core/repository/DefaultMeetingRepository.kt @@ -3,7 +3,6 @@ package com.mulberry.ody.data.remote.core.repository import com.mulberry.ody.data.local.db.MateEtaInfoDao import com.mulberry.ody.data.local.entity.eta.MateEtaInfoEntity import com.mulberry.ody.data.remote.core.entity.meeting.mapper.toMateEtaInfo -import com.mulberry.ody.data.remote.core.entity.meeting.mapper.toMeeting import com.mulberry.ody.data.remote.core.entity.meeting.mapper.toMeetingCatalogs import com.mulberry.ody.data.remote.core.entity.meeting.mapper.toMeetingRequest import com.mulberry.ody.data.remote.core.entity.meeting.mapper.toNudgeRequest @@ -11,12 +10,15 @@ import com.mulberry.ody.data.remote.core.entity.meeting.request.MatesEtaRequest import com.mulberry.ody.data.remote.core.service.MeetingService import com.mulberry.ody.domain.apiresult.ApiResult import com.mulberry.ody.domain.apiresult.map +import com.mulberry.ody.domain.model.Mate import com.mulberry.ody.domain.model.MateEtaInfo import com.mulberry.ody.domain.model.Meeting import com.mulberry.ody.domain.model.MeetingCatalog import com.mulberry.ody.domain.model.MeetingCreationInfo import com.mulberry.ody.domain.model.Nudge import com.mulberry.ody.domain.repository.ody.MeetingRepository +import java.time.LocalDate +import java.time.LocalTime import javax.inject.Inject class DefaultMeetingRepository @@ -30,7 +32,21 @@ class DefaultMeetingRepository } override suspend fun fetchMeeting(meetingId: Long): ApiResult { - return service.fetchMeeting(meetingId).map { it.toMeeting() } + return ApiResult.Success( + Meeting( + id = 1, + name = "약속 이름", + date = LocalDate.of(2025, 2, 1), + time = LocalTime.of(11, 30), + destinationAddress = "서울특별시 강남구 테헤란로 411 (성담빌딩)", + departureAddress = "서울특별시 송파구 올림픽로 35다길 (한국루터회관)", + departureTime = LocalTime.of(10, 0), + routeTime = 70, + mates = listOf(Mate("올리브1", ""), Mate("올리브2", "")), + inviteCode = "12345", + ), + ) + // return service.fetchMeeting(meetingId).map { it.toMeeting() } } override suspend fun postNudge(nudge: Nudge): ApiResult = service.postNudge(nudge.toNudgeRequest()) diff --git a/android/app/src/main/java/com/mulberry/ody/data/remote/core/service/MeetingService.kt b/android/app/src/main/java/com/mulberry/ody/data/remote/core/service/MeetingService.kt index 0d657faa4..1aa3749c3 100644 --- a/android/app/src/main/java/com/mulberry/ody/data/remote/core/service/MeetingService.kt +++ b/android/app/src/main/java/com/mulberry/ody/data/remote/core/service/MeetingService.kt @@ -35,7 +35,7 @@ interface MeetingService { @GET("/v1/meetings/me") suspend fun fetchMeetingCatalogs(): ApiResult - @GET("/v1/meetings/{meetingId}") + @GET("/v2/meetings/{meetingId}") suspend fun fetchMeeting( @Path(value = "meetingId") meetingId: Long, ): ApiResult diff --git a/android/app/src/main/java/com/mulberry/ody/domain/model/Meeting.kt b/android/app/src/main/java/com/mulberry/ody/domain/model/Meeting.kt index eb19fa35f..9afda1ad9 100644 --- a/android/app/src/main/java/com/mulberry/ody/domain/model/Meeting.kt +++ b/android/app/src/main/java/com/mulberry/ody/domain/model/Meeting.kt @@ -6,9 +6,12 @@ import java.time.LocalTime data class Meeting( val id: Long, val name: String, - val targetPosition: String, - val meetingDate: LocalDate, - val meetingTime: LocalTime, + val date: LocalDate, + val time: LocalTime, + val destinationAddress: String, + val departureAddress: String, + val departureTime: LocalTime, + val routeTime: Int, val mates: List, val inviteCode: String, ) diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/common/DimensionExtensions.kt b/android/app/src/main/java/com/mulberry/ody/presentation/common/DimensionExtensions.kt index c201dc0f7..b1487d16e 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/common/DimensionExtensions.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/common/DimensionExtensions.kt @@ -1,8 +1,16 @@ package com.mulberry.ody.presentation.common import android.content.Context +import android.graphics.Point +import android.view.View fun Int.toPixel(context: Context): Int { val density = context.resources.displayMetrics.density return (this * density).toInt() } + +fun View.getPointOnScreen(): Point { + val location = IntArray(2) + this.getLocationOnScreen(location) + return Point(location[0], location[1]) +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/join/MeetingJoinActivity.kt b/android/app/src/main/java/com/mulberry/ody/presentation/join/MeetingJoinActivity.kt index 12f3cc841..0ec7a93fb 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/join/MeetingJoinActivity.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/join/MeetingJoinActivity.kt @@ -18,6 +18,7 @@ import com.mulberry.ody.presentation.common.listener.BackListener import com.mulberry.ody.presentation.common.listener.NextListener import com.mulberry.ody.presentation.join.complete.JoinCompleteActivity import com.mulberry.ody.presentation.room.MeetingRoomActivity +import com.mulberry.ody.presentation.room.MeetingRoomActivity.Companion.NAVIGATE_TO_DETAIL_MEETING import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -90,7 +91,7 @@ class MeetingJoinActivity : MeetingRoomActivity.getIntent( this, meetingId, - MeetingRoomActivity.NAVIGATE_TO_NOTIFICATION_LOG, + NAVIGATE_TO_DETAIL_MEETING, ) startActivity(intent) finish() diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/meetings/MeetingsActivity.kt b/android/app/src/main/java/com/mulberry/ody/presentation/meetings/MeetingsActivity.kt index 64a8ba5b6..78861fd1c 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/meetings/MeetingsActivity.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/meetings/MeetingsActivity.kt @@ -19,6 +19,8 @@ import com.mulberry.ody.presentation.login.LoginActivity import com.mulberry.ody.presentation.meetings.adapter.MeetingsAdapter import com.mulberry.ody.presentation.meetings.listener.MeetingsListener import com.mulberry.ody.presentation.room.MeetingRoomActivity +import com.mulberry.ody.presentation.room.MeetingRoomActivity.Companion.NAVIGATE_TO_DETAIL_MEETING +import com.mulberry.ody.presentation.room.MeetingRoomActivity.Companion.NAVIGATE_TO_ETA_DASHBOARD import com.mulberry.ody.presentation.setting.SettingActivity import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -116,7 +118,7 @@ class MeetingsActivity : MeetingRoomActivity.getIntent( this, meetingId, - MeetingRoomActivity.NAVIGATE_TO_NOTIFICATION_LOG, + NAVIGATE_TO_DETAIL_MEETING, ) startActivity(intent) } @@ -126,7 +128,7 @@ class MeetingsActivity : MeetingRoomActivity.getIntent( this, meetingId, - MeetingRoomActivity.NAVIGATE_TO_ETA_DASHBOARD, + NAVIGATE_TO_ETA_DASHBOARD, ) startActivity(intent) } @@ -137,7 +139,7 @@ class MeetingsActivity : } override fun guideItemDisabled() { - showSnackBar(R.string.meetings_entrance_unavailable_guide) + showSnackBar(R.string.inaccessible_eta_guide) } override fun onClickSetting() { diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/notification/FCMNotification.kt b/android/app/src/main/java/com/mulberry/ody/presentation/notification/FCMNotification.kt index 20b89b8a4..c81ae0611 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/notification/FCMNotification.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/notification/FCMNotification.kt @@ -12,8 +12,8 @@ import com.mulberry.ody.data.local.db.OdyDatastore import com.mulberry.ody.domain.model.NotificationType import com.mulberry.ody.presentation.meetings.MeetingsActivity import com.mulberry.ody.presentation.room.MeetingRoomActivity +import com.mulberry.ody.presentation.room.MeetingRoomActivity.Companion.NAVIGATE_TO_DETAIL_MEETING import com.mulberry.ody.presentation.room.MeetingRoomActivity.Companion.NAVIGATE_TO_ETA_DASHBOARD -import com.mulberry.ody.presentation.room.MeetingRoomActivity.Companion.NAVIGATE_TO_NOTIFICATION_LOG import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first @@ -93,7 +93,7 @@ class FCMNotification ): PendingIntent? { val navigationTarget = when (type) { - NotificationType.ENTRY, NotificationType.DEPARTURE_REMINDER -> NAVIGATE_TO_NOTIFICATION_LOG + NotificationType.ENTRY, NotificationType.DEPARTURE_REMINDER -> NAVIGATE_TO_DETAIL_MEETING NotificationType.NUDGE, NotificationType.ETA_NOTICE -> NAVIGATE_TO_ETA_DASHBOARD NotificationType.DEFAULT -> "" } diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomActivity.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomActivity.kt index bab4ff575..b60c4a296 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomActivity.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomActivity.kt @@ -12,7 +12,10 @@ import com.mulberry.ody.databinding.ActivityMeetingRoomBinding import com.mulberry.ody.presentation.collectWhenStarted import com.mulberry.ody.presentation.common.binding.BindingActivity import com.mulberry.ody.presentation.common.listener.BackListener +import com.mulberry.ody.presentation.room.detail.DetailMeetingFragment import com.mulberry.ody.presentation.room.etadashboard.EtaDashboardFragment +import com.mulberry.ody.presentation.room.listener.MeetingRoomListener +import com.mulberry.ody.presentation.room.log.ExitMeetingRoomDialog import com.mulberry.ody.presentation.room.log.NotificationLogFragment import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -20,7 +23,8 @@ import javax.inject.Inject @AndroidEntryPoint class MeetingRoomActivity : BindingActivity(R.layout.activity_meeting_room), - BackListener { + BackListener, + MeetingRoomListener { @Inject lateinit var viewModelFactory: MeetingRoomViewModel.MeetingViewModelFactory @@ -30,6 +34,7 @@ class MeetingRoomActivity : private val fragments: Map by lazy { mapOf( + NAVIGATE_TO_DETAIL_MEETING to DetailMeetingFragment(), NAVIGATE_TO_ETA_DASHBOARD to EtaDashboardFragment(), NAVIGATE_TO_NOTIFICATION_LOG to NotificationLogFragment(), ) @@ -54,8 +59,13 @@ class MeetingRoomActivity : private fun initializeObserve() { onBackPressedDispatcher.addCallback(this, onBackPressedCallback) - collectWhenStarted(viewModel.navigateToEtaDashboardEvent) { - addFragment(EtaDashboardFragment()) + collectWhenStarted(viewModel.navigationEvent) { + val fragment = + when (it) { + MeetingRoomNavigateAction.NavigateToEtaDashboard -> fragments[NAVIGATE_TO_ETA_DASHBOARD] + MeetingRoomNavigateAction.NavigateToNotificationLog -> fragments[NAVIGATE_TO_NOTIFICATION_LOG] + } ?: return@collectWhenStarted + addFragment(fragment) } collectWhenStarted(viewModel.networkErrorEvent) { showRetrySnackBar { viewModel.retryLastAction() } @@ -73,6 +83,9 @@ class MeetingRoomActivity : } hideLoadingDialog() } + collectWhenStarted(viewModel.inaccessibleEtaEvent) { + showSnackBar(R.string.inaccessible_eta_guide) + } } private fun initializeFragment() { @@ -102,14 +115,22 @@ class MeetingRoomActivity : private fun getMeetingId(): Long = intent.getLongExtra(MEETING_ID_KEY, MEETING_ID_DEFAULT_VALUE) - private fun getNavigateView(): String = intent.getStringExtra(NAVIGATE_VIEW_KEY) ?: NAVIGATE_TO_NOTIFICATION_LOG + private fun getNavigateView(): String = intent.getStringExtra(NAVIGATE_VIEW_KEY) ?: NAVIGATE_TO_DETAIL_MEETING + + override fun onExitMeetingRoom() { + ExitMeetingRoomDialog().show(supportFragmentManager, EXIT_MEETING_ROOM_DIALOG_TAG) + } companion object { + private const val EXIT_MEETING_ROOM_DIALOG_TAG = "exitMeetingRoomDialog" + private const val MEETING_ID_KEY = "meeting_id" private const val MEETING_ID_DEFAULT_VALUE = -1L + private const val NAVIGATE_VIEW_KEY = "navigate_view" + const val NAVIGATE_TO_DETAIL_MEETING = "detail_meeting" const val NAVIGATE_TO_ETA_DASHBOARD = "eta_dashboard" - const val NAVIGATE_TO_NOTIFICATION_LOG = "notification_log" + private const val NAVIGATE_TO_NOTIFICATION_LOG = "notification_log" fun getIntent( context: Context, diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomNavigateAction.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomNavigateAction.kt new file mode 100644 index 000000000..512d9090a --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomNavigateAction.kt @@ -0,0 +1,7 @@ +package com.mulberry.ody.presentation.room + +sealed interface MeetingRoomNavigateAction { + data object NavigateToEtaDashboard : MeetingRoomNavigateAction + + data object NavigateToNotificationLog : MeetingRoomNavigateAction +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomViewModel.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomViewModel.kt index abe9738c0..0d91fdd28 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomViewModel.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/MeetingRoomViewModel.kt @@ -22,15 +22,15 @@ import com.mulberry.ody.presentation.common.analytics.logButtonClicked import com.mulberry.ody.presentation.common.analytics.logNetworkErrorEvent import com.mulberry.ody.presentation.common.image.ImageShareContent import com.mulberry.ody.presentation.common.image.ImageShareHelper +import com.mulberry.ody.presentation.room.detail.model.DetailMeetingUiModel +import com.mulberry.ody.presentation.room.detail.model.InviteCodeCopyInfo +import com.mulberry.ody.presentation.room.detail.model.MateUiModel +import com.mulberry.ody.presentation.room.detail.model.toDetailMeetingUiModel +import com.mulberry.ody.presentation.room.detail.model.toMateUiModels import com.mulberry.ody.presentation.room.etadashboard.listener.NudgeListener import com.mulberry.ody.presentation.room.etadashboard.model.MateEtaUiModel import com.mulberry.ody.presentation.room.etadashboard.model.toMateEtaUiModels -import com.mulberry.ody.presentation.room.log.model.InviteCodeCopyInfo -import com.mulberry.ody.presentation.room.log.model.MateUiModel -import com.mulberry.ody.presentation.room.log.model.MeetingDetailUiModel import com.mulberry.ody.presentation.room.log.model.NotificationLogUiModel -import com.mulberry.ody.presentation.room.log.model.toMateUiModels -import com.mulberry.ody.presentation.room.log.model.toMeetingUiModel import com.mulberry.ody.presentation.room.log.model.toNotificationUiModels import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -74,9 +74,8 @@ class MeetingRoomViewModel initialValue = null, ) - private val _meeting: MutableStateFlow = - MutableStateFlow(MeetingDetailUiModel.DEFAULT) - val meeting: StateFlow = _meeting.asStateFlow() + private val _meeting: MutableStateFlow = MutableStateFlow(DetailMeetingUiModel.DEFAULT) + val meeting: StateFlow = _meeting.asStateFlow() private val _mates: MutableStateFlow> = MutableStateFlow(listOf()) val mates: StateFlow> = _mates.asStateFlow() @@ -84,8 +83,8 @@ class MeetingRoomViewModel private val _notificationLogs = MutableStateFlow>(listOf()) val notificationLogs: StateFlow> = _notificationLogs.asStateFlow() - private val _navigateToEtaDashboardEvent: MutableSharedFlow = MutableSharedFlow() - val navigateToEtaDashboardEvent: SharedFlow get() = _navigateToEtaDashboardEvent.asSharedFlow() + private val _navigationEvent: MutableSharedFlow = MutableSharedFlow() + val navigationEvent: SharedFlow get() = _navigationEvent.asSharedFlow() private val _nudgeSuccessMate: MutableSharedFlow = MutableSharedFlow() val nudgeSuccessMate: SharedFlow get() = _nudgeSuccessMate.asSharedFlow() @@ -104,6 +103,12 @@ class MeetingRoomViewModel private val matesNudgeTimes: MutableMap = mutableMapOf() + private val _isVisibleNavigation: MutableStateFlow = MutableStateFlow(false) + val isVisibleNavigation: StateFlow get() = _isVisibleNavigation + + private val _inaccessibleEtaEvent: MutableSharedFlow = MutableSharedFlow() + val inaccessibleEtaEvent: SharedFlow get() = _inaccessibleEtaEvent + init { fetchMeeting() } @@ -180,7 +185,7 @@ class MeetingRoomViewModel startLoading() meetingRepository.fetchMeeting(meetingId) .onSuccess { - _meeting.value = it.toMeetingUiModel() + _meeting.value = it.toDetailMeetingUiModel() _mates.value = it.toMateUiModels() fetchNotificationLogs() }.onFailure { code, errorMessage -> @@ -196,12 +201,27 @@ class MeetingRoomViewModel } fun navigateToEtaDashboard() { + viewModelScope.launch { + if (!meeting.value.isEtaAccessible()) { + _inaccessibleEtaEvent.emit(Unit) + return@launch + } + + analyticsHelper.logButtonClicked( + eventName = "navigate_to_eta_dashboard", + location = TAG, + ) + _navigationEvent.emit(MeetingRoomNavigateAction.NavigateToEtaDashboard) + } + } + + fun navigateToNotificationLog() { viewModelScope.launch { analyticsHelper.logButtonClicked( - eventName = "eta_button_from_notification_log", + eventName = "navigate_to_notification_log", location = TAG, ) - _navigateToEtaDashboardEvent.emit(Unit) + _navigationEvent.emit(MeetingRoomNavigateAction.NavigateToNotificationLog) } } @@ -288,6 +308,10 @@ class MeetingRoomViewModel } } + fun handleNavigationVisibility() { + _isVisibleNavigation.value = !_isVisibleNavigation.value + } + @AssistedFactory interface MeetingViewModelFactory { fun create(meetingId: Long): MeetingRoomViewModel diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/DetailMeetingBindingAdapter.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/DetailMeetingBindingAdapter.kt new file mode 100644 index 000000000..178b6f58d --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/DetailMeetingBindingAdapter.kt @@ -0,0 +1,27 @@ +package com.mulberry.ody.presentation.room.detail + +import android.widget.ImageView +import android.widget.TextView +import androidx.databinding.BindingAdapter +import com.mulberry.ody.R +import com.mulberry.ody.presentation.common.getPointOnScreen +import com.mulberry.ody.presentation.room.detail.listener.DepartureTimeGuideListener +import com.mulberry.ody.presentation.room.detail.model.DetailMeetingUiModel + +@BindingAdapter("onClickDepartureTimeGuide") +fun ImageView.setOnClickDepartureTimeGuide(missingToolTipListener: DepartureTimeGuideListener) { + setOnClickListener { + val point = this.getPointOnScreen() + missingToolTipListener.toggleDepartureTimeGuide(point) + } +} + +@BindingAdapter("departureTime") +fun TextView.setDepartureTimeText(detailMeetingUiModel: DetailMeetingUiModel) { + text = + context.getString( + R.string.detail_meeting_departure_time_content, + detailMeetingUiModel.departureTime, + detailMeetingUiModel.routeTime, + ) +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/DetailMeetingFragment.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/DetailMeetingFragment.kt index 723e2835e..eaeb228dc 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/DetailMeetingFragment.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/DetailMeetingFragment.kt @@ -1,13 +1,38 @@ package com.mulberry.ody.presentation.room.detail +import android.app.Activity +import android.content.Context.LAYOUT_INFLATER_SERVICE +import android.content.Intent +import android.graphics.Point import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup +import android.widget.PopupWindow +import androidx.fragment.app.activityViewModels import com.mulberry.ody.R -import com.mulberry.ody.databinding.FragmentNotificationLogBinding +import com.mulberry.ody.databinding.FragmentDetailMeetingBinding +import com.mulberry.ody.databinding.LayoutDepartureTimeTooltipBinding +import com.mulberry.ody.presentation.collectWhenStarted import com.mulberry.ody.presentation.common.binding.BindingFragment +import com.mulberry.ody.presentation.common.toPixel +import com.mulberry.ody.presentation.room.MeetingRoomActivity +import com.mulberry.ody.presentation.room.MeetingRoomViewModel +import com.mulberry.ody.presentation.room.detail.adapter.MatesAdapter +import com.mulberry.ody.presentation.room.detail.listener.DepartureTimeGuideListener +import com.mulberry.ody.presentation.room.detail.listener.InviteCodeCopyListener +import com.mulberry.ody.presentation.room.detail.model.InviteCodeCopyUiModel +import com.mulberry.ody.presentation.room.detail.model.MatesUiModel class DetailMeetingFragment : - BindingFragment(R.layout.fragment_detail_meeting) { + BindingFragment(R.layout.fragment_detail_meeting), + DepartureTimeGuideListener, + InviteCodeCopyListener { + private val viewModel: MeetingRoomViewModel by activityViewModels() + private val parentActivity: Activity by lazy { requireActivity() } + private val matesAdapter: MatesAdapter by lazy { MatesAdapter(this) } + override fun onViewCreated( view: View, savedInstanceState: Bundle?, @@ -18,8 +43,83 @@ class DetailMeetingFragment : } fun initializeBinding() { + binding.vm = viewModel + binding.backListener = parentActivity as MeetingRoomActivity + binding.meetingRoomListener = parentActivity as MeetingRoomActivity + binding.departureTimeGuideListener = this + binding.rvDetailMeetingMates.adapter = matesAdapter } private fun initializeObserve() { + collectWhenStarted(viewModel.mates) { + val mates: List = it + listOf(InviteCodeCopyUiModel()) + matesAdapter.submitList(mates) + } + collectWhenStarted(viewModel.copyInviteCodeEvent) { + val intent = Intent(Intent.ACTION_SEND_MULTIPLE) + intent.type = "text/plain" + + val shareMessage = + getString(R.string.invite_code_copy, it.meetingName, it.inviteCode) + intent.putExtra(Intent.EXTRA_TEXT, shareMessage) + + val chooserTitle = getString(R.string.invite_code_copy_title) + startActivity(Intent.createChooser(intent, chooserTitle)) + } + } + + override fun toggleDepartureTimeGuide(point: Point) { + val inflater = parentActivity.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater + val parentViewGroup = parentActivity.findViewById(android.R.id.content) + val binding = LayoutDepartureTimeTooltipBinding.inflate(inflater, parentViewGroup, false) + val popupView = binding.root + + popupView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + + val adjustedPoint = calculateDepartureTimeGuidePoint(popupView, point) + showPopup(adjustedPoint, popupView, parentActivity.window.decorView) + } + + private fun calculateDepartureTimeGuidePoint( + popupView: View, + guideButtonPoint: Point, + ): Point { + val popupHeight = popupView.measuredHeight + + val guideButtonWidthPixel = GUIDE_BUTTON_WIDTH.toPixel(binding.root.context) + val guideButtonPaddingPixel = GUIDE_BUTTON_PADDING.toPixel(binding.root.context) + val adjustedX = guideButtonPoint.x + guideButtonWidthPixel + guideButtonPaddingPixel + val adjustedY = guideButtonPoint.y - popupHeight + guideButtonPaddingPixel + + return Point(adjustedX, adjustedY) + } + + private fun showPopup( + point: Point, + popupView: View, + decorView: View, + ) { + val popupWindow = + PopupWindow( + popupView, + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + true, + ) + popupWindow.showAtLocation( + decorView, + Gravity.NO_GRAVITY, + point.x, + point.y, + ) + } + + override fun onCopy() { + viewModel.copyInviteCode() + } + + companion object { + private const val GUIDE_BUTTON_PADDING = 5 + private const val GUIDE_BUTTON_WIDTH = 12 } } diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/adapter/MatesAdapter.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/adapter/MatesAdapter.kt new file mode 100644 index 000000000..d223aa447 --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/adapter/MatesAdapter.kt @@ -0,0 +1,64 @@ +package com.mulberry.ody.presentation.room.detail.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import com.mulberry.ody.databinding.ItemInviteCodeCopyBinding +import com.mulberry.ody.databinding.ItemMateBinding +import com.mulberry.ody.presentation.room.detail.listener.InviteCodeCopyListener +import com.mulberry.ody.presentation.room.detail.model.MateUiModel +import com.mulberry.ody.presentation.room.detail.model.MatesUiModel +import java.lang.IllegalStateException + +class MatesAdapter( + private val inviteCodeCopyListener: InviteCodeCopyListener, +) : + ListAdapter(DiffCallback()) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): MatesViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + MatesUiModel.MATE_VIEW_TYPE -> { + val binding = ItemMateBinding.inflate(inflater, parent, false) + MateViewHolder(binding) + } + + MatesUiModel.INVITE_CODE_COPY_VIEW_TYPE -> { + val binding = ItemInviteCodeCopyBinding.inflate(inflater, parent, false) + InviteCodeCopyViewHolder(binding) + } + + else -> throw IllegalStateException("뷰 타입에 해당하는 뷰홀더가 존재하지 않습니다.") + } + } + + override fun onBindViewHolder( + holder: MatesViewHolder, + position: Int, + ) { + when (holder) { + is MateViewHolder -> holder.bind(getItem(position) as MateUiModel) + is InviteCodeCopyViewHolder -> holder.bind(inviteCodeCopyListener) + } + } + + override fun getItemViewType(position: Int): Int { + return getItem(position).viewType + } + + class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: MatesUiModel, + newItem: MatesUiModel, + ): Boolean = oldItem == newItem + + override fun areContentsTheSame( + oldItem: MatesUiModel, + newItem: MatesUiModel, + ): Boolean = oldItem == newItem + } +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/adapter/MatesViewHolder.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/adapter/MatesViewHolder.kt new file mode 100644 index 000000000..8533c8f39 --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/adapter/MatesViewHolder.kt @@ -0,0 +1,26 @@ +package com.mulberry.ody.presentation.room.detail.adapter + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.mulberry.ody.databinding.ItemInviteCodeCopyBinding +import com.mulberry.ody.databinding.ItemMateBinding +import com.mulberry.ody.presentation.room.detail.listener.InviteCodeCopyListener +import com.mulberry.ody.presentation.room.detail.model.MateUiModel + +sealed class MatesViewHolder(view: View) : RecyclerView.ViewHolder(view) + +class MateViewHolder(private val binding: ItemMateBinding) : + MatesViewHolder(binding.root) { + fun bind(mate: MateUiModel) { + binding.mate = mate + } +} + +class InviteCodeCopyViewHolder( + private val binding: ItemInviteCodeCopyBinding, +) : + MatesViewHolder(binding.root) { + fun bind(inviteCodeCopyListener: InviteCodeCopyListener) { + binding.inviteCodeCopyListener = inviteCodeCopyListener + } +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/listener/DepartureTimeGuideListener.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/listener/DepartureTimeGuideListener.kt new file mode 100644 index 000000000..d860cb760 --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/listener/DepartureTimeGuideListener.kt @@ -0,0 +1,7 @@ +package com.mulberry.ody.presentation.room.detail.listener + +import android.graphics.Point + +interface DepartureTimeGuideListener { + fun toggleDepartureTimeGuide(point: Point) +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/listener/InviteCodeCopyListener.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/listener/InviteCodeCopyListener.kt new file mode 100644 index 000000000..023d178c7 --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/listener/InviteCodeCopyListener.kt @@ -0,0 +1,5 @@ +package com.mulberry.ody.presentation.room.detail.listener + +interface InviteCodeCopyListener { + fun onCopy() +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/DetailMeetingUiModel.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/DetailMeetingUiModel.kt new file mode 100644 index 000000000..8f84e47e8 --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/DetailMeetingUiModel.kt @@ -0,0 +1,42 @@ +package com.mulberry.ody.presentation.room.detail.model + +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +data class DetailMeetingUiModel( + val id: Long, + val name: String, + val dateTime: String, + val destinationAddress: String, + val departureAddress: String, + val departureTime: String, + val routeTime: String, + val mates: List, + val inviteCode: String, +) { + fun isEtaAccessible(): Boolean { + val formatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN) + val localDateTime = LocalDateTime.parse(dateTime, formatter) + return localDateTime.minusMinutes(ETA_ACCESSIBLE_MINUTE) <= LocalDateTime.now() + } + + fun isDefault(): Boolean = this == DEFAULT + + companion object { + const val DATE_TIME_PATTERN = "yyyy년 M월 d일 HH시 mm분" + private const val ETA_ACCESSIBLE_MINUTE = 30L + + val DEFAULT: DetailMeetingUiModel = + DetailMeetingUiModel( + id = -1L, + name = "-", + dateTime = "0", + destinationAddress = "-", + departureAddress = "-", + departureTime = "-", + routeTime = "-", + mates = listOf("-"), + inviteCode = "", + ) + } +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/DetailMeetingUiModelMapper.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/DetailMeetingUiModelMapper.kt new file mode 100644 index 000000000..69cbea062 --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/DetailMeetingUiModelMapper.kt @@ -0,0 +1,40 @@ +package com.mulberry.ody.presentation.room.detail.model + +import com.mulberry.ody.domain.model.Meeting +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.format.DateTimeFormatter + +fun Meeting.toDetailMeetingUiModel(): DetailMeetingUiModel { + return DetailMeetingUiModel( + id = id, + name = name, + dateTime = this.toDateTimeString(), + destinationAddress = departureAddress, + departureAddress = destinationAddress, + departureTime = departureTime.toTimeString(), + routeTime = routeTime.toTimeString(), + mates = mates.map { it.nickname }, + inviteCode = inviteCode, + ) +} + +private fun Meeting.toDateTimeString(): String { + val dateTime = LocalDateTime.of(date, time) + val dateTimeFormatter = DateTimeFormatter.ofPattern(DetailMeetingUiModel.DATE_TIME_PATTERN) + return dateTime.format(dateTimeFormatter) +} + +private fun LocalTime.toTimeString(): String { + val dateTimeFormatter = DateTimeFormatter.ofPattern("HH시 mm분") + return format(dateTimeFormatter) +} + +private fun Int.toTimeString(): String { + val hour = this / 60 + val minute = this % 60 + val localTime = LocalTime.of(hour, minute) + val formatterPattern = if (hour == 0) "mm분" else "HH시 mm분" + val dateTimeFormatter = DateTimeFormatter.ofPattern(formatterPattern) + return localTime.format(dateTimeFormatter) +} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/InviteCodeCopyInfo.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/InviteCodeCopyInfo.kt similarity index 60% rename from android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/InviteCodeCopyInfo.kt rename to android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/InviteCodeCopyInfo.kt index a15fff9c4..b42311608 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/InviteCodeCopyInfo.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/InviteCodeCopyInfo.kt @@ -1,4 +1,4 @@ -package com.mulberry.ody.presentation.room.log.model +package com.mulberry.ody.presentation.room.detail.model class InviteCodeCopyInfo( val meetingName: String, diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MateUiModelMapper.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/MateUiModelMapper.kt similarity index 81% rename from android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MateUiModelMapper.kt rename to android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/MateUiModelMapper.kt index 4d267329c..f4003025b 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MateUiModelMapper.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/MateUiModelMapper.kt @@ -1,4 +1,4 @@ -package com.mulberry.ody.presentation.room.log.model +package com.mulberry.ody.presentation.room.detail.model import com.mulberry.ody.domain.model.Mate import com.mulberry.ody.domain.model.Meeting diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/MatesUiModel.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/MatesUiModel.kt new file mode 100644 index 000000000..92a319dba --- /dev/null +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/detail/model/MatesUiModel.kt @@ -0,0 +1,23 @@ +package com.mulberry.ody.presentation.room.detail.model + +import com.mulberry.ody.presentation.room.detail.model.MatesUiModel.Companion.INVITE_CODE_COPY_VIEW_TYPE +import com.mulberry.ody.presentation.room.detail.model.MatesUiModel.Companion.MATE_VIEW_TYPE + +sealed interface MatesUiModel { + val viewType: Int + + companion object { + const val MATE_VIEW_TYPE = 0 + const val INVITE_CODE_COPY_VIEW_TYPE = 1 + } +} + +class MateUiModel( + val nickname: String, + val imageUrl: String, + override val viewType: Int = MATE_VIEW_TYPE, +) : MatesUiModel + +class InviteCodeCopyUiModel( + override val viewType: Int = INVITE_CODE_COPY_VIEW_TYPE, +) : MatesUiModel diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/etadashboard/EtaDashboardBindingAdapter.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/etadashboard/EtaDashboardBindingAdapter.kt index 5d13d8d7b..3cc2c5c9e 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/etadashboard/EtaDashboardBindingAdapter.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/etadashboard/EtaDashboardBindingAdapter.kt @@ -1,12 +1,11 @@ package com.mulberry.ody.presentation.room.etadashboard -import android.graphics.Point -import android.view.View import android.view.animation.AnimationUtils import android.widget.TextView import androidx.core.content.ContextCompat import androidx.databinding.BindingAdapter import com.mulberry.ody.R +import com.mulberry.ody.presentation.common.getPointOnScreen import com.mulberry.ody.presentation.common.listener.setOnSingleClickListener import com.mulberry.ody.presentation.room.etadashboard.listener.MissingToolTipListener import com.mulberry.ody.presentation.room.etadashboard.listener.NudgeListener @@ -29,7 +28,7 @@ fun TextView.setOnClickMissingTooltip( isUserSelf: Boolean, missingToolTipListener: MissingToolTipListener, ) { - setOnSingleClickListener { + setOnClickListener { val point = this.getPointOnScreen() missingToolTipListener.onClickMissingToolTipListener(point, isUserSelf) } @@ -65,12 +64,3 @@ fun TextView.setOnClickNudge( } } } - -private fun View.getPointOnScreen(): Point { - val location = IntArray(2) - this.getLocationOnScreen(location) - return Point().apply { - x = location[0] - y = location[1] - } -} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/NotificationLogFragment.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/log/NotificationLogFragment.kt index dc19450a9..f665e80d0 100644 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/NotificationLogFragment.kt +++ b/android/app/src/main/java/com/mulberry/ody/presentation/room/log/NotificationLogFragment.kt @@ -1,6 +1,5 @@ package com.mulberry.ody.presentation.room.log -import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels @@ -10,16 +9,12 @@ import com.mulberry.ody.presentation.collectWhenStarted import com.mulberry.ody.presentation.common.binding.BindingFragment import com.mulberry.ody.presentation.room.MeetingRoomActivity import com.mulberry.ody.presentation.room.MeetingRoomViewModel -import com.mulberry.ody.presentation.room.listener.MeetingRoomListener -import com.mulberry.ody.presentation.room.log.adapter.MatesAdapter import com.mulberry.ody.presentation.room.log.adapter.NotificationLogsAdapter class NotificationLogFragment : - BindingFragment(R.layout.fragment_notification_log), - MeetingRoomListener { + BindingFragment(R.layout.fragment_notification_log) { private val viewModel: MeetingRoomViewModel by activityViewModels() private val notificationLogsAdapter: NotificationLogsAdapter by lazy { NotificationLogsAdapter() } - private val matesAdapter: MatesAdapter by lazy { MatesAdapter() } override fun onViewCreated( view: View, @@ -33,7 +28,7 @@ class NotificationLogFragment : fun initializeBinding() { binding.vm = viewModel binding.backListener = requireActivity() as MeetingRoomActivity - binding.meetingRoomListener = this + binding.meetingRoomListener = requireActivity() as MeetingRoomActivity binding.rvNotificationLog.adapter = notificationLogsAdapter } @@ -41,29 +36,5 @@ class NotificationLogFragment : collectWhenStarted(viewModel.notificationLogs) { notificationLogsAdapter.submitList(it) } - collectWhenStarted(viewModel.mates) { - matesAdapter.submitList(it) - } - collectWhenStarted(viewModel.copyInviteCodeEvent) { - viewModel.copyInviteCodeEvent.collect { - val intent = Intent(Intent.ACTION_SEND_MULTIPLE) - intent.type = "text/plain" - - val shareMessage = - getString(R.string.invite_code_copy, it.meetingName, it.inviteCode) - intent.putExtra(Intent.EXTRA_TEXT, shareMessage) - - val chooserTitle = getString(R.string.invite_code_copy_title) - startActivity(Intent.createChooser(intent, chooserTitle)) - } - } - } - - override fun onExitMeetingRoom() { - ExitMeetingRoomDialog().show(parentFragmentManager, EXIT_MEETING_ROOM_DIALOG_TAG) - } - - companion object { - private const val EXIT_MEETING_ROOM_DIALOG_TAG = "exitMeetingRoomDialog" } } diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/adapter/MateViewHolder.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/log/adapter/MateViewHolder.kt deleted file mode 100644 index 4a0360d47..000000000 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/adapter/MateViewHolder.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.mulberry.ody.presentation.room.log.adapter - -import androidx.recyclerview.widget.RecyclerView -import com.mulberry.ody.databinding.ItemMateBinding -import com.mulberry.ody.presentation.room.log.model.MateUiModel - -class MateViewHolder(private val binding: ItemMateBinding) : - RecyclerView.ViewHolder(binding.root) { - fun bind(mate: MateUiModel) { - binding.mate = mate - } -} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/adapter/MatesAdapter.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/log/adapter/MatesAdapter.kt deleted file mode 100644 index a097fc4d8..000000000 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/adapter/MatesAdapter.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.mulberry.ody.presentation.room.log.adapter - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import com.mulberry.ody.databinding.ItemMateBinding -import com.mulberry.ody.presentation.room.log.model.MateUiModel - -class MatesAdapter : - ListAdapter(DiffCallback()) { - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int, - ): MateViewHolder { - val binding = - ItemMateBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return MateViewHolder(binding) - } - - override fun onBindViewHolder( - holder: MateViewHolder, - position: Int, - ) { - holder.bind(getItem(position)) - } - - class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: MateUiModel, - newItem: MateUiModel, - ): Boolean = oldItem == newItem - - override fun areContentsTheSame( - oldItem: MateUiModel, - newItem: MateUiModel, - ): Boolean = oldItem == newItem - } -} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MateUiModel.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MateUiModel.kt deleted file mode 100644 index 3ada9f328..000000000 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MateUiModel.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.mulberry.ody.presentation.room.log.model - -data class MateUiModel( - val nickname: String, - val imageUrl: String, -) diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MeetingDetailUiModel.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MeetingDetailUiModel.kt deleted file mode 100644 index 29a433593..000000000 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MeetingDetailUiModel.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.mulberry.ody.presentation.room.log.model - -data class MeetingDetailUiModel( - val id: Long, - val name: String, - val targetPosition: String, - val meetingTime: String, - val mates: List, - val inviteCode: String, - val isEtaAccessible: Boolean, -) { - fun isDefault(): Boolean = this == DEFAULT - - companion object { - val DEFAULT: MeetingDetailUiModel = - MeetingDetailUiModel( - id = -1L, - name = "-", - targetPosition = "-", - meetingTime = "-", - mates = listOf("-"), - inviteCode = "", - isEtaAccessible = false, - ) - } -} diff --git a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MeetingDetailUiModelMapper.kt b/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MeetingDetailUiModelMapper.kt deleted file mode 100644 index 1f17192b3..000000000 --- a/android/app/src/main/java/com/mulberry/ody/presentation/room/log/model/MeetingDetailUiModelMapper.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.mulberry.ody.presentation.room.log.model - -import com.mulberry.ody.domain.model.Meeting -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.format.DateTimeFormatter - -fun Meeting.toMeetingUiModel(): MeetingDetailUiModel { - val meetingDateTime = LocalDateTime.of(meetingDate, meetingTime) - - return MeetingDetailUiModel( - id, - name, - targetPosition, - toMeetingDateTime(meetingDate, meetingTime), - mates.map { it.nickname }, - inviteCode, - meetingDateTime.minusMinutes(30) <= LocalDateTime.now(), - ) -} - -private fun toMeetingDateTime( - meetingDate: LocalDate, - meetingTime: LocalTime, -): String { - val dateTime = LocalDateTime.of(meetingDate, meetingTime) - val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 M월 d일 HH시 mm분") - return dateTime.format(dateTimeFormatter) -} diff --git a/android/app/src/main/res/drawable/selector_detail_meeting_button.xml b/android/app/src/main/res/drawable/selector_detail_meeting_button.xml index 0408887be..2c2f99631 100644 --- a/android/app/src/main/res/drawable/selector_detail_meeting_button.xml +++ b/android/app/src/main/res/drawable/selector_detail_meeting_button.xml @@ -1,5 +1,5 @@ - + diff --git a/android/app/src/main/res/layout/fragment_detail_meeting.xml b/android/app/src/main/res/layout/fragment_detail_meeting.xml index 18f0c8f2f..39d4f53cd 100644 --- a/android/app/src/main/res/layout/fragment_detail_meeting.xml +++ b/android/app/src/main/res/layout/fragment_detail_meeting.xml @@ -5,22 +5,41 @@ + + + + + + + + + android:layout_height="match_parent" + android:background="@color/primary"> + app:meetingRoomListener="@{meetingRoomListener}" + app:title="@{vm.meeting.name}" /> + app:layout_constraintTop_toBottomOf="@id/tv_detail_meeting_address" /> + app:layout_constraintBottom_toBottomOf="@id/detail_meeting_departure_time_title" + app:layout_constraintStart_toEndOf="@id/detail_meeting_departure_time_title" + app:layout_constraintTop_toTopOf="@id/detail_meeting_departure_time_title" + app:onClickDepartureTimeGuide="@{departureTimeGuideListener}" /> + app:layout_constraintTop_toTopOf="@id/btn_detail_meeting" + app:onSingleClick="@{() -> vm.navigateToEtaDashboard()}" + app:visibility="@{vm.isVisibleNavigation}" + tools:visibility="visible"> + + + app:layout_constraintTop_toTopOf="@id/btn_detail_meeting" + app:onSingleClick="@{() -> vm.navigateToNotificationLog()}" + app:visibility="@{vm.isVisibleNavigation}" + tools:visibility="visible"> + + android:layout_marginEnd="22dp" + android:layout_marginBottom="10dp"> + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> + app:layout_constraintTop_toBottomOf="@id/tv_invite_code_guide" + app:layout_constraintVertical_chainStyle="packed" + app:onSingleClick="@{() -> inviteCodeCopyListener.onCopy()}" /> diff --git a/android/app/src/main/res/layout/item_mate.xml b/android/app/src/main/res/layout/item_mate.xml index 26778ca45..f5321f3ec 100644 --- a/android/app/src/main/res/layout/item_mate.xml +++ b/android/app/src/main/res/layout/item_mate.xml @@ -7,7 +7,7 @@ + type="com.mulberry.ody.presentation.room.detail.model.MateUiModel" /> 알림 권한을 거절하실 경우 푸시 알람을 받으실 수 없어요 알림 권한은 앱 설정에서 확인할 수 있어요 위치 권한을 거절하면 내 위치를 친구에게 공유할 수 없어요 - 약속 시간 30분 전부터 실시간 위치를 확인할 수 있어요 "앱을 종료하려면 뒤로 가기를 한 번 더 눌려주세요." 약속 만들기 약속 참여하기 @@ -166,8 +165,8 @@ 약속 장소 출발 장소 늦지 않으려면 - 출발 시간은 대중교통 지연 등을 고려해\n 여유 있게 알려드리고 있어요. - %s에 나가야 해요.\n출발 장소부터 약속 장소까지 %s걸려요. + 출발 시간은 대중교통 지연 등을 고려해\n여유 있게 알려드리고 있어요. + %s에 나가야 해요.\n출발 장소부터 약속 장소까지 %s 걸려요. 약속 로그 친구를 초대해 보세요! 초대 코드 복사하기 @@ -183,6 +182,7 @@ 현재 위치를 가져올 수 없어요. 잠시 후 다시 시도해 주세요. 오류가 발생했습니다. 잠시 후 다시 시도해 주세요. 네트워크 연결 확인 후 다시 시도해 주세요. + 약속 시간 30분 전부터 실시간 위치를 확인할 수 있어요 재시도 오디? diff --git a/android/app/src/test/java/com/mulberry/ody/TestFixtures.kt b/android/app/src/test/java/com/mulberry/ody/TestFixtures.kt index a085742c0..3bc12ce36 100644 --- a/android/app/src/test/java/com/mulberry/ody/TestFixtures.kt +++ b/android/app/src/test/java/com/mulberry/ody/TestFixtures.kt @@ -20,17 +20,18 @@ val meetingId: Long = 0L val meeting: Meeting = Meeting( - 0, - "meetingA", - "선릉 캠퍼스", - LocalDate.of(2024, 1, 1), - LocalTime.of(10, 0), - listOf(Mate("A", ""), Mate("B", ""), Mate("C", "")), - inviteCode, + id = meetingId, + name = "meetingA", + destinationAddress = "선릉 캠퍼스", + departureAddress = "잠실 캠퍼스", + date = LocalDate.of(2024, 1, 1), + time = LocalTime.of(10, 0), + departureTime = LocalTime.of(2, 30), + routeTime = 30, + mates = listOf(Mate("A", ""), Mate("B", ""), Mate("C", "")), + inviteCode = inviteCode, ) -val meetings: List = listOf(meeting) - val meetingCatalog = MeetingCatalog( meetingId, diff --git a/android/app/src/test/java/com/mulberry/ody/presentation/room/MeetingRoomViewModelTest.kt b/android/app/src/test/java/com/mulberry/ody/presentation/room/MeetingRoomViewModelTest.kt index 34849ba78..ea71d5ab4 100644 --- a/android/app/src/test/java/com/mulberry/ody/presentation/room/MeetingRoomViewModelTest.kt +++ b/android/app/src/test/java/com/mulberry/ody/presentation/room/MeetingRoomViewModelTest.kt @@ -11,8 +11,8 @@ import com.mulberry.ody.mateEtaInfo import com.mulberry.ody.meeting import com.mulberry.ody.meetingId import com.mulberry.ody.notificationLogs +import com.mulberry.ody.presentation.room.detail.model.toDetailMeetingUiModel import com.mulberry.ody.presentation.room.etadashboard.model.toMateEtaUiModels -import com.mulberry.ody.presentation.room.log.model.toMeetingUiModel import com.mulberry.ody.presentation.room.log.model.toNotificationUiModels import com.mulberry.ody.util.CoroutinesTestExtension import com.mulberry.ody.util.InstantTaskExecutorExtension @@ -65,7 +65,7 @@ class MeetingRoomViewModelTest { runTest { // then val meetingUiModel = viewModel.meeting.first() - assertThat(meetingUiModel).isEqualTo(meeting.toMeetingUiModel()) + assertThat(meetingUiModel).isEqualTo(meeting.toDetailMeetingUiModel()) val notificationLogUiModel = viewModel.notificationLogs.first() assertThat(notificationLogUiModel).isEqualTo(notificationLogs.toNotificationUiModels())