diff --git a/docs/qa/assumptions.md b/docs/qa/assumptions.md index 0e2a212d3c..1f2e206a56 100644 --- a/docs/qa/assumptions.md +++ b/docs/qa/assumptions.md @@ -259,6 +259,17 @@ Compose 테스트로 고정했다. 스키마에 `date` 없음, 보내도 무시됨을 실측). 날짜 선택이 표시 전용인 문제는 BE 계약 확장이나 기획 결정이 필요해 #1008 로 분리했다. +## #1008 — 일기 기록 날짜를 표시 전용으로 (판단 근거) + +서버가 `date` 를 받지 않는다는 것은 추측이 아니라 실측이다 (2026-08-25). + +- `POST /api/v1/diary` 에 `date: "2026-08-01"` → 저장분 `date: "2026-08-25"` (diaryId 41545) +- `PATCH /api/v1/diary/41545` 에 같은 값 → 저장분 그대로 `2026-08-25` +- Swagger 스키마의 요청 필드: `title · content · isDraft · todayMood · receiverIds` (양쪽 모두 `date` 없음) + +**추측인 부분은 «어느 쪽으로 맞출지» 다.** 이슈가 든 두 선택지 중 (b) 표시 전용을 택했다 — +(a) BE 확장은 우리 손 밖이고, 그때까지 «고를 수 있지만 반영되지 않는» 상태를 두면 사용자가 +고른 날짜로 저장됐다고 믿는다. BE 가 `date` 를 수용하면 피커를 되돌린다. ## #613 — 수신자 홈 설정 진입 (판단 보류) - 시안(수신자 섹션 `4327:73626`)의 수신자 홈 헤더에는 **톱니만** 있고 프로필 아이콘이 없다. diff --git a/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/screen/sender/DiaryWriteScreen.kt b/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/screen/sender/DiaryWriteScreen.kt index 2a53f0a096..d66b0ca99a 100644 --- a/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/screen/sender/DiaryWriteScreen.kt +++ b/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/screen/sender/DiaryWriteScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextField @@ -46,7 +45,6 @@ import com.afternote.core.ui.theme.AfternoteDesign import com.afternote.core.ui.theme.AfternoteTheme import com.afternote.core.ui.topbar.DetailTopBar import com.afternote.feature.mindrecord.domain.model.TodayMood -import com.afternote.feature.mindrecord.presentation.component.BottomSheetCalendar import com.afternote.feature.mindrecord.presentation.component.ReceiverSelectBottomSheet import com.afternote.feature.mindrecord.presentation.component.WriteTextField import com.afternote.feature.mindrecord.presentation.viewmodel.DiaryWriteViewModel @@ -65,7 +63,6 @@ fun DiaryWriteScreen( viewModel: DiaryWriteViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - var showPicker by remember { mutableStateOf(false) } var showReceiverSheet by remember { mutableStateOf(false) } val currentOnSubmitSuccess by rememberUpdatedState(onSubmitSuccess) @@ -169,11 +166,15 @@ fun DiaryWriteScreen( } // Figma 2671:17922 — 날짜 선택 행 ("yyyy년 M월 d일" + 아래 화살표) + // **표시 전용이다.** 서버는 생성·수정 어느 쪽에서도 date 를 받지 않고 기록 날짜를 + // 요청 시각으로 정한다 (실측 2026-08-25: POST·PATCH 에 date 를 실어도 저장분은 + // 오늘, Swagger 스키마에도 필드가 없다). 고를 수 있게 두면 사용자는 고른 날짜로 + // 남았다고 믿는데 실제로는 오늘로 저장된다 — 고르지 못하게 해 거짓을 없앤다 (#1008). + // + // BE 가 date 를 수용하면 이 자리에 피커를 되돌린다. 이어쓰기 프리필이 채워 주는 + // 원래 날짜를 보여 주는 역할은 그대로다. Row( - modifier = - Modifier - .fillMaxWidth() - .clickable { showPicker = !showPicker }, + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { @@ -182,12 +183,6 @@ fun DiaryWriteScreen( style = AfternoteDesign.typography.captionLargeR, color = AfternoteDesign.colors.gray9, ) - IconButton(onClick = { showPicker = !showPicker }) { - Icon( - painter = painterResource(R.drawable.core_ui_arrowdown), - contentDescription = null, - ) - } } HorizontalDivider() @@ -307,17 +302,6 @@ fun DiaryWriteScreen( ) } - if (showPicker) { - BottomSheetCalendar( - initialDate = uiState.date, - onDismiss = { showPicker = false }, - onDateSelect = { date -> - viewModel.onDateSelected(date) - showPicker = false - }, - ) - } - if (showReceiverSheet) { ReceiverSelectBottomSheet( receivers = uiState.receivers, diff --git a/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteUiState.kt b/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteUiState.kt index bcb1082cfa..096fbc9d59 100644 --- a/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteUiState.kt +++ b/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteUiState.kt @@ -9,6 +9,11 @@ data class DiaryWriteUiState( val title: String = "", val content: String = "", val mood: TodayMood? = null, + /** + * 화면에 표시할 기록 날짜. **표시 전용이다** — 서버는 생성·수정 어느 쪽에서도 이 값을 + * 받지 않고 기록 날짜를 요청 시각으로 정한다 (#1008). 이어쓰기 프리필이 원래 날짜를 + * 채워 주는 자리라 상태 자체는 남긴다. + */ val date: LocalDate = LocalDate.now(), val imageUrl: String? = null, /** `GET /users/receivers` 로 불러온 내 수신인 목록. 로드 실패 시 빈 목록 (작성은 가능). */ diff --git a/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteViewModel.kt b/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteViewModel.kt index 68754836c8..0f0abe9c23 100644 --- a/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteViewModel.kt +++ b/feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/DiaryWriteViewModel.kt @@ -83,10 +83,6 @@ class DiaryWriteViewModel _uiState.update { it.copy(mood = mood) } } - fun onDateSelected(date: LocalDate) { - _uiState.update { it.copy(date = date) } - } - fun onReceiverToggled(receiverId: Long) { _uiState.update { val selected = it.selectedReceiverIds @@ -228,9 +224,8 @@ class DiaryWriteViewModel title = draft.title, content = draft.content, mood = draft.todayMood, - // 서버가 날짜를 주지 않은 임시저장이면 날짜 선택기의 현재 값을 - // 유지한다 — 표시용으로 오늘을 지어내는 것과 달리, 여기서는 - // 사용자가 등록 전에 직접 고르는 값이다. + // 표시 전용 값이다 — 서버가 날짜를 주면 그걸 보여 주고, 안 주면 + // 화면에 이미 떠 있던 값을 유지한다. 고르는 수단은 없다 (#1008). date = draft.toUi()?.date ?: it.date, imageUrl = draft.imageUrl, isDraftLoading = false, diff --git a/feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/screen/DiaryWriteDateDisplayTest.kt b/feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/screen/DiaryWriteDateDisplayTest.kt new file mode 100644 index 0000000000..0d1528bf16 --- /dev/null +++ b/feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/screen/DiaryWriteDateDisplayTest.kt @@ -0,0 +1,35 @@ +package com.afternote.feature.mindrecord.presentation.screen + +import com.afternote.feature.mindrecord.presentation.viewmodel.DiaryWriteViewModel +import org.junit.Assert.assertTrue +import org.junit.Test +import java.time.LocalDate + +/** + * 일기 기록 날짜가 «고를 수 있지만 반영되지 않는» 상태가 아닌지 (#1008). + * + * 서버는 생성·수정 어느 쪽에서도 `date` 를 받지 않고 기록 날짜를 요청 시각으로 정한다 + * (실측 2026-08-25: `POST`·`PATCH` 에 `date: "2026-08-01"` 을 실어 보내도 저장분은 + * `2026-08-25`, Swagger 스키마에도 필드가 없다). 그 상태에서 날짜 피커를 열어 두면 + * 사용자는 고른 날짜로 남았다고 믿는다. + * + * 화면에서 피커를 걷어냈으므로 **ViewModel 에 날짜를 바꾸는 창구가 남아 있으면 안 된다** — + * 남겨 두면 다음 사람이 그 함수를 보고 UI 를 되살린다. BE 가 `date` 를 수용하면 그때 함께 + * 되돌린다. + */ +class DiaryWriteDateDisplayTest { + @Test + fun `날짜를 바꾸는 공개 창구가 없다`() { + // "update" 가 "date" 를 부분 문자열로 품는다 — 날짜와 무관한 updateXxx 가 하나만 + // 생겨도 빨개지므로, 이름 규칙이 아니라 **LocalDate 를 받는 공개 함수**로 좁힌다. + val dateSetters = + DiaryWriteViewModel::class.java.methods + .filter { + java.lang.reflect.Modifier + .isPublic(it.modifiers) + }.filter { it.parameterTypes.any { type -> type == LocalDate::class.java } } + .map { it.name } + + assertTrue("날짜 변경 창구가 남아 있다: $dateSetters", dateSetters.isEmpty()) + } +}