From 5291198f8cfd9fd0acbe2881ad0b9014703df902 Mon Sep 17 00:00:00 2001 From: princehw03 Date: Thu, 12 Feb 2026 20:22:14 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=EB=8B=AC=EB=A0=A5=EC=B0=BD=20?= =?UTF-8?q?=EB=82=98=EC=98=AC=20=EB=95=8C=20=EC=9D=BC=EA=B8=B0=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EC=97=90=20=ED=95=B4=EB=8B=B9=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=8B=AC=EC=9D=B4=20=EB=82=98=EC=98=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=8E=B8=EC=9D=98=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egobook/app/ui/diary/view/CalenderFragment.kt | 12 ++++++++++++ .../com/egobook/app/ui/diary/view/DiaryFragment.kt | 11 ++++++++++- app/src/main/res/navigation/bottom_navigation.xml | 10 ++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt b/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt index 4bd3ba9c..057a4eda 100644 --- a/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt @@ -63,6 +63,7 @@ class CalenderFragment : Fragment() { v.setPadding(v.paddingLeft, v.paddingTop, v.paddingRight, systemBars.bottom) insets } + applyInitialMonthFromArgs() setupDayOfWeekTitles() setupCalendar() @@ -102,6 +103,17 @@ class CalenderFragment : Fragment() { } } } + + private fun applyInitialMonthFromArgs() { + val args = arguments + val year = args?.getInt("year", -1) ?: -1 + val month = args?.getInt("month", -1) ?: -1 + + if (year != -1 && month != -1) { + viewModel.setYearMonth(YearMonth.of(year, month)) + arguments = null // 재적용 방지 + } + } /** * MonthDialogFragment에서 월 선택 결과 수신 diff --git a/app/src/main/java/com/egobook/app/ui/diary/view/DiaryFragment.kt b/app/src/main/java/com/egobook/app/ui/diary/view/DiaryFragment.kt index 6d2d114e..59782a64 100644 --- a/app/src/main/java/com/egobook/app/ui/diary/view/DiaryFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/diary/view/DiaryFragment.kt @@ -113,8 +113,17 @@ import kotlin.getValue } } btnCalender.setOnClickListener { - findNavController().navigate(R.id.action_diaryFragment_to_calenderFragment) + val date = viewModel.state.value.selectedDate + + val action = DiaryFragmentDirections + .actionDiaryFragmentToCalenderFragment( + year = date.year, + month = date.monthValue + ) + + findNavController().navigate(action) } + btnExport.setOnClickListener { applyScreenBlur(BlurLevel.BASE) val dialog = DiaryExportDialogFragment() diff --git a/app/src/main/res/navigation/bottom_navigation.xml b/app/src/main/res/navigation/bottom_navigation.xml index be937d7c..731dc5b5 100644 --- a/app/src/main/res/navigation/bottom_navigation.xml +++ b/app/src/main/res/navigation/bottom_navigation.xml @@ -113,6 +113,16 @@ android:id="@+id/calenderFragment" android:name="com.egobook.app.ui.diary.view.CalenderFragment" android:label="달력 화면" > + + + + Date: Thu, 12 Feb 2026 20:45:40 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=EB=8B=AC=EB=A0=A5=20=EC=9B=94=20?= =?UTF-8?q?=ED=94=BD=EC=8A=A4=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egobook/app/data/api/DiaryApiService.kt | 9 +++++++++ .../app/data/model/diary/request/DiaryExportRequest.kt | 4 ++++ .../data/model/diary/response/DiaryExportResponse.kt | 10 ++++++++++ .../com/egobook/app/ui/diary/view/CalenderFragment.kt | 4 ++++ .../app/ui/diary/viewmodel/CalenderViewModel.kt | 9 ++++++++- 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/egobook/app/data/model/diary/response/DiaryExportResponse.kt diff --git a/app/src/main/java/com/egobook/app/data/api/DiaryApiService.kt b/app/src/main/java/com/egobook/app/data/api/DiaryApiService.kt index 33c16b8a..4618ed95 100644 --- a/app/src/main/java/com/egobook/app/data/api/DiaryApiService.kt +++ b/app/src/main/java/com/egobook/app/data/api/DiaryApiService.kt @@ -2,11 +2,14 @@ package com.egobook.app.data.api import com.egobook.app.data.model.ApiResponse import com.egobook.app.data.model.diary.request.DiaryCreateRequest +import com.egobook.app.data.model.diary.request.DiaryExportRequest import com.egobook.app.data.model.diary.request.DiaryUpdateRequest import com.egobook.app.data.model.diary.response.DiariesResponse import com.egobook.app.data.model.diary.response.DiaryCreateResponse import com.egobook.app.data.model.diary.response.DiaryDeleteResponse import com.egobook.app.data.model.diary.response.DiaryEntryResponse +import com.egobook.app.data.model.diary.response.DiaryExportResponse +import com.google.android.gms.common.api.Api import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET @@ -51,4 +54,10 @@ interface DiaryApiService { @Body request: DiaryUpdateRequest ): ApiResponse + //일기 내보내기 + @POST("/diaries/export") + suspend fun exportDiary( + @Body request: DiaryExportRequest + ): ApiResponse + } \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/data/model/diary/request/DiaryExportRequest.kt b/app/src/main/java/com/egobook/app/data/model/diary/request/DiaryExportRequest.kt index 16fd737f..4989d725 100644 --- a/app/src/main/java/com/egobook/app/data/model/diary/request/DiaryExportRequest.kt +++ b/app/src/main/java/com/egobook/app/data/model/diary/request/DiaryExportRequest.kt @@ -7,5 +7,9 @@ import kotlinx.serialization.Serializable data class DiaryExportRequest ( @SerialName("format") val format: String, + @SerialName("startDate") + val startDate: String, + @SerialName("endDate") + val endDate: String, ) \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/data/model/diary/response/DiaryExportResponse.kt b/app/src/main/java/com/egobook/app/data/model/diary/response/DiaryExportResponse.kt new file mode 100644 index 00000000..3f52f18c --- /dev/null +++ b/app/src/main/java/com/egobook/app/data/model/diary/response/DiaryExportResponse.kt @@ -0,0 +1,10 @@ +package com.egobook.app.data.model.diary.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class DiaryExportResponse( + @SerialName("fileUrl") + val fileUrl: String, +) diff --git a/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt b/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt index 057a4eda..7612a8dc 100644 --- a/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt @@ -110,8 +110,12 @@ class CalenderFragment : Fragment() { val month = args?.getInt("month", -1) ?: -1 if (year != -1 && month != -1) { + // 초기 년월 설정 후 명시적 로드 viewModel.setYearMonth(YearMonth.of(year, month)) arguments = null // 재적용 방지 + } else { + // 인자가 없으면 현재 월 로드 + viewModel.initializeCalendar() } } diff --git a/app/src/main/java/com/egobook/app/ui/diary/viewmodel/CalenderViewModel.kt b/app/src/main/java/com/egobook/app/ui/diary/viewmodel/CalenderViewModel.kt index b8592ef2..6cb594bd 100644 --- a/app/src/main/java/com/egobook/app/ui/diary/viewmodel/CalenderViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/diary/viewmodel/CalenderViewModel.kt @@ -34,10 +34,17 @@ class CalenderViewModel @Inject constructor( get() = _state.value.selectedYearMonth /** - * 초기 로드 + * 초기 로드 - 외부에서 명시적으로 호출 필요 */ init { Log.d("ViewModel1", "=== ViewModel INIT === selectedYearMonth=${_state.value.selectedYearMonth}") + // init에서 자동 로드하지 않음 - Fragment에서 초기 년월 설정 후 명시적 호출 + } + + /** + * 초기 캘린더 데이터 로드 (Fragment에서 명시적 호출) + */ + fun initializeCalendar() { loadCalender(_state.value.selectedYearMonth) } From 9eb5ac46813932d87d23b595af8e7af6b2d2a678 Mon Sep 17 00:00:00 2001 From: princehw03 Date: Thu, 12 Feb 2026 20:51:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=EB=8B=AC=EB=A0=A5=20=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EC=A0=84=EA=B9=8C=EC=A7=80=20invisible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/diary/view/CalenderFragment.kt | 75 ++++++++++++------- app/src/main/res/layout/fragment_calender.xml | 7 +- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt b/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt index 7612a8dc..a13bfe15 100644 --- a/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/diary/view/CalenderFragment.kt @@ -11,6 +11,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.children +import androidx.core.view.isInvisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -35,6 +36,7 @@ import java.time.YearMonth import java.time.format.TextStyle import java.util.Locale import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import timber.log.Timber @@ -63,11 +65,22 @@ class CalenderFragment : Fragment() { v.setPadding(v.paddingLeft, v.paddingTop, v.paddingRight, systemBars.bottom) insets } - applyInitialMonthFromArgs() - + + // 1. 먼저 초기 년월 결정 (인자 있으면 그걸로, 없으면 현재 달) + val initialMonth = getInitialMonthFromArgs() + + // 2. 캘린더를 처음에는 숨김 (깜빡임 방지) + binding.calendarView.visibility = View.INVISIBLE + binding.calendarHeaderLayout.visibility = View.INVISIBLE + setupDayOfWeekTitles() - setupCalendar() - observeViewModel() + setupCalendar(initialMonth) // 초기 년월 전달 + + // 3. ViewModel을 초기 년월로 설정 (데이터 로드) + viewModel.setYearMonth(initialMonth) + + // 4. observing 시작 + observeViewModel(initialMonth) // 초기 년월 전달해서 첫 emission 검증 setupMonthDialogResultListener() binding.apply { @@ -104,21 +117,21 @@ class CalenderFragment : Fragment() { } } - private fun applyInitialMonthFromArgs() { + /** + * 초기 년월을 인자에서 추출 (없으면 현재 달 반환) + */ + private fun getInitialMonthFromArgs(): YearMonth { val args = arguments val year = args?.getInt("year", -1) ?: -1 val month = args?.getInt("month", -1) ?: -1 - if (year != -1 && month != -1) { - // 초기 년월 설정 후 명시적 로드 - viewModel.setYearMonth(YearMonth.of(year, month)) - arguments = null // 재적용 방지 + return if (year != -1 && month != -1) { + YearMonth.of(year, month) } else { - // 인자가 없으면 현재 월 로드 - viewModel.initializeCalendar() + YearMonth.now() } } - + /** * MonthDialogFragment에서 월 선택 결과 수신 */ @@ -137,10 +150,17 @@ class CalenderFragment : Fragment() { /** * ViewModel 상태 관찰 - 스와이프 없이 해당 월 즉시 표시 */ - private fun observeViewModel() { + private fun observeViewModel(expectedInitialMonth: YearMonth) { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { + var isCorrectMonthReceived = false viewModel.state.collectLatest { state -> + // 예상한 초기 달이 나올 때까지 대기 (깜빡임 방지) + if (!isCorrectMonthReceived && state.selectedYearMonth != expectedInitialMonth) { + return@collectLatest // 잘못된 달은 무시 + } + isCorrectMonthReceived = true + // 애니메이션 없이 해당 월 즉시 이동 binding.calendarView.scrollToMonth(state.selectedYearMonth) // 상단 텍스트 업데이트 @@ -148,6 +168,12 @@ class CalenderFragment : Fragment() { binding.tvMonth.text = "${state.selectedYearMonth.monthValue}월" // 중요: 감정 데이터 변경 시 해당 월만 캘린더 뷰 갱신 binding.calendarView.notifyMonthChanged(state.selectedYearMonth) + + // 첫 번째 올바른 상태 업데이트 후 캘린더 표시 + if (binding.calendarView.isInvisible) { + binding.calendarView.visibility = View.VISIBLE + binding.calendarHeaderLayout.visibility = View.VISIBLE + } } } } @@ -194,33 +220,30 @@ class CalenderFragment : Fragment() { /** * 달력 설정 및 초기화 */ - private fun setupCalendar() { - val currentMonth = YearMonth.now() - - initializeCalendarRange(currentMonth) - initializeYearMonthText(currentMonth) + private fun setupCalendar(initialMonth: YearMonth) { + initializeCalendarRange(initialMonth) + initializeYearMonthText(initialMonth) setupDayBinder() - // 초기 로드는 ViewModel의 init 블록에서 처리 } /** * 달력 범위 설정 (과거 100개월 ~ 미래 100개월) */ - private fun initializeCalendarRange(currentMonth: YearMonth) { - val startMonth = currentMonth.minusMonths(100) - val endMonth = currentMonth.plusMonths(100) + private fun initializeCalendarRange(initialMonth: YearMonth) { + val startMonth = initialMonth.minusMonths(100) + val endMonth = initialMonth.plusMonths(100) val firstDayOfWeek = DayOfWeek.MONDAY binding.calendarView.setup(startMonth, endMonth, firstDayOfWeek) - binding.calendarView.scrollToMonth(currentMonth) + binding.calendarView.scrollToMonth(initialMonth) } /** * 초기 연/월 텍스트 설정 */ - private fun initializeYearMonthText(currentMonth: YearMonth) { - binding.tvYear.text = currentMonth.year.toString() - binding.tvMonth.text = "${currentMonth.monthValue}월" + private fun initializeYearMonthText(initialMonth: YearMonth) { + binding.tvYear.text = initialMonth.year.toString() + binding.tvMonth.text = "${initialMonth.monthValue}월" } /** diff --git a/app/src/main/res/layout/fragment_calender.xml b/app/src/main/res/layout/fragment_calender.xml index 89abf74c..bfe21deb 100644 --- a/app/src/main/res/layout/fragment_calender.xml +++ b/app/src/main/res/layout/fragment_calender.xml @@ -49,6 +49,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" + android:visibility="invisible" app:layout_constraintTop_toBottomOf="@id/top_btn_layout" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"> @@ -58,7 +59,8 @@ android:id="@+id/tv_year" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="2025" + android:text="" + tools:text="2025" android:textColor="@color/cos_black" android:fontFamily="@font/arita_medium" android:textSize="14sp" /> @@ -86,7 +88,8 @@ android:id="@+id/tv_month" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="12월" + android:text="" + tools:text="12월" android:textSize="24sp" android:layout_marginHorizontal="24dp" android:fontFamily="@font/arita_semibold" />