diff --git a/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt b/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt index 615bd6a..68c65d7 100644 --- a/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt +++ b/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt @@ -181,23 +181,27 @@ class DiaryRepositoryImpl @Inject constructor( } override suspend fun deleteDiary(accessToken: String, id: Int): Result { - val result = diaryRemoteDataSource.deleteDiary(accessToken, id) + return try { + val result = diaryRemoteDataSource.deleteDiary(accessToken, id) - return if (result.isSuccess) { - val res = result.getOrNull() - if (res != null) { - val data = res.data - if (data != null) { - val msg = DeleteDiaryMsg(data.message) - Result.success(msg) + if (result.isSuccess) { + val res = result.getOrNull() + if (res != null) { + val data = res.data + if (data != null) { + val msg = DeleteDiaryMsg(data.message) + Result.success(msg) + } else { + Result.failure(Exception("Delete Diary Failed: data is null")) + } } else { - Result.failure(Exception("Delete Diary Failed: data is null")) + Result.failure(Exception("Delete Diary Failed: response body is null")) } } else { - Result.failure(Exception("Delete Diary Failed: response body is null")) + Result.failure(result.exceptionOrNull() ?: Exception("Unknown error")) } - } else { - Result.failure(result.exceptionOrNull() ?: Exception("Unknown error")) + } catch (e: Exception) { + Result.failure(e) } } } \ No newline at end of file diff --git a/Nabi/domain/src/main/java/com/nabi/domain/extension/EmotionStateUtils.kt b/Nabi/domain/src/main/java/com/nabi/domain/extension/EmotionStateUtils.kt new file mode 100644 index 0000000..77c96fe --- /dev/null +++ b/Nabi/domain/src/main/java/com/nabi/domain/extension/EmotionStateUtils.kt @@ -0,0 +1,8 @@ +package com.nabi.domain.extension + +fun String.parseEmotionState(): String { + // ' ' 사이의 감정 텍스트만 뽑아서 return + val regex = """'([^']*)'""".toRegex() + val matchResult = regex.find(this) + return matchResult?.groups?.get(1)?.value ?: "" +} \ No newline at end of file diff --git a/Nabi/domain/src/main/java/com/nabi/domain/utils/EmotionStateUtils.kt b/Nabi/domain/src/main/java/com/nabi/domain/utils/EmotionStateUtils.kt deleted file mode 100644 index 37f2983..0000000 --- a/Nabi/domain/src/main/java/com/nabi/domain/utils/EmotionStateUtils.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.nabi.domain.utils - -object EmotionStateUtils { - - fun parseEmotionState(emotionState: String): String { - // ' ' 사이의 감정 텍스트만 뽑아서 return - val regex = """'([^']*)'""".toRegex() - val matchResult = regex.find(emotionState) - return matchResult?.groups?.get(1)?.value ?: "" - } -} \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthCalendarStateAdapter.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthCalendarStateAdapter.kt index 10b24a6..065ff75 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthCalendarStateAdapter.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthCalendarStateAdapter.kt @@ -7,28 +7,12 @@ import com.nabi.domain.model.diary.DiarySelectInfo import java.util.Calendar class AddDiaryMonthCalendarStateAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { - private lateinit var onDateSelectedListener: OnDateSelectedListener override fun getItemCount(): Int = Int.MAX_VALUE override fun createFragment(position: Int): Fragment { val calendar = Calendar.getInstance().apply { add(Calendar.MONTH, position - (Int.MAX_VALUE / 2)) } - val fragment = AddDiaryMonthFragment.newInstance(calendar.time).apply { - setOnDateSelectedListener(object : AddDiaryMonthFragment.OnDateSelectedListener { - override fun onDateSelected(item: DiarySelectInfo) { - onDateSelectedListener.onDateSelected(item) - } - }) - } - return fragment - } - - fun setOnDateSelectedListener(onDateSelectedListener: OnDateSelectedListener) { - this.onDateSelectedListener = onDateSelectedListener - } - - interface OnDateSelectedListener { - fun onDateSelected(item: DiarySelectInfo) + return AddDiaryMonthFragment.newInstance(calendar.time) } } \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthFragment.kt index 0e3218b..592b826 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthFragment.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryMonthFragment.kt @@ -2,18 +2,23 @@ package com.nabi.nabi.views.diary.add import android.os.Bundle import androidx.fragment.app.activityViewModels +import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import com.nabi.domain.model.diary.DiarySelectInfo import com.nabi.nabi.R import com.nabi.nabi.base.BaseFragment import com.nabi.nabi.databinding.FragmentAddDiaryMonthBinding import com.nabi.nabi.utils.Constants.dateEnglishOnlyYearFormat +import com.nabi.nabi.utils.Constants.dateKoreanFormat import com.nabi.nabi.utils.Constants.dateNumberOnlyMonthFormat import com.nabi.nabi.utils.LoggerUtils import com.nabi.nabi.utils.UiState import com.nabi.nabi.views.OnRvItemClickListener import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date @@ -28,8 +33,6 @@ class AddDiaryMonthFragment : private lateinit var dayAdapter: AddDiaryCalendarAdapter private lateinit var date: Date - - private var onDateSelectedListener: OnDateSelectedListener? = null private var diaryDates: Set = emptySet() private var isClickable = true @@ -53,12 +56,14 @@ class AddDiaryMonthFragment : } private fun setupRecyclerView() { + val diaryDays = getDaysInMonth(date) + dayAdapter = AddDiaryCalendarAdapter().apply { - setRvItemClickListener( object : OnRvItemClickListener{ + setRvItemClickListener(object : OnRvItemClickListener { override fun onClick(item: DiarySelectInfo) { sharedDateViewModel.changeSelectedDate(item.diaryEntryDate) isClickable = !diaryDates.contains(item.diaryEntryDate) - onDateSelectedListener?.onDateSelected(item) + sharedDateViewModel.changeDateInfo(item) } }) } @@ -68,6 +73,14 @@ class AddDiaryMonthFragment : adapter = dayAdapter itemAnimator = null } + dayAdapter.setList( + matchDiaryEntriesWithDays( + List(diaryDays.size) { null }, + diaryDays, + year = dateEnglishOnlyYearFormat.format(date.time).toInt(), + month = dateNumberOnlyMonthFormat.format(date.time).toInt() + ) + ) } private fun loadDiaryData() { @@ -124,10 +137,15 @@ class AddDiaryMonthFragment : val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) val todayDate = dateFormat.format(Calendar.getInstance().time) - if (todayDate == dateString) { - matchedDiaryInfo.isSelected = true + if (sharedDateViewModel.date.value != null) { + if (sharedDateViewModel.date.value!!.diaryEntryDate == dateString) { + matchedDiaryInfo.isSelected = true + } + } else { + if (todayDate == dateString) { + matchedDiaryInfo.isSelected = true + } } - result.add(dateString to matchedDiaryInfo) } } @@ -170,12 +188,4 @@ class AddDiaryMonthFragment : updatedList.find { it.first == selectedDate }?.second?.isSelected = true dayAdapter.setList(updatedList) } - - fun setOnDateSelectedListener(listener: OnDateSelectedListener) { - this.onDateSelectedListener = listener - } - - interface OnDateSelectedListener { - fun onDateSelected(item: DiarySelectInfo) - } } \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt index 080c9cb..ad0d9bf 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt @@ -33,6 +33,7 @@ class AddDiarySelectDateFragment : private lateinit var calendarAdapter: AddDiaryMonthCalendarStateAdapter + private var lastVisibleDate: Calendar? = null private var diaryDates: Set = emptySet() private lateinit var selectedDate: DiarySelectInfo private var tempDiary: DiaryDbEntity? = null @@ -43,6 +44,19 @@ class AddDiarySelectDateFragment : override fun onResume() { super.onResume() updateDisplayedDate() + + lastVisibleDate = getCurrentVisibleDate() + lastVisibleDate?.let { date -> + val position = calculatePositionFromDate(date) + calendarAdapter = AddDiaryMonthCalendarStateAdapter(requireActivity()) + binding.vpCalendarMonth.adapter = calendarAdapter + binding.vpCalendarMonth.setCurrentItem(position, false) + updateCurrentMonthText(position) + } ?: run { + updateCurrentMonthText(binding.vpCalendarMonth.currentItem) + } + + updateSelectedDate(selectedDate.diaryEntryDate) } override fun initView() { @@ -55,18 +69,7 @@ class AddDiarySelectDateFragment : } selectedDate = DiarySelectInfo(false, sharedViewModel.selectedDate.value!!, true) - LoggerUtils.d(selectedDate.toString()) - - calendarAdapter = AddDiaryMonthCalendarStateAdapter(requireActivity()).apply { - setOnDateSelectedListener(object : - AddDiaryMonthCalendarStateAdapter.OnDateSelectedListener { - override fun onDateSelected(item: DiarySelectInfo) { - selectedDate = item - updateSelectedDate(item.diaryEntryDate) - } - }) - } - + calendarAdapter = AddDiaryMonthCalendarStateAdapter(requireActivity()) binding.apply { vpCalendarMonth.adapter = calendarAdapter vpCalendarMonth.setCurrentItem(Int.MAX_VALUE / 2, false) @@ -99,6 +102,13 @@ class AddDiarySelectDateFragment : else -> Unit } } + + sharedViewModel.date.observe(viewLifecycleOwner) { + if (it != null) { + selectedDate = it + updateSelectedDate(it.diaryEntryDate) + } + } } private fun updateDisplayedDate() { @@ -237,4 +247,19 @@ class AddDiarySelectDateFragment : binding.tvSelectYear.text = calendar.get(Calendar.YEAR).toString() binding.tvSelectMonth.text = dateEnglishOnlyMonthFormat.format(calendar.time) } + + private fun getCurrentVisibleDate(): Calendar { + val calendar = Calendar.getInstance().apply { + add(Calendar.MONTH, binding.vpCalendarMonth.currentItem - (Int.MAX_VALUE / 2)) + } + return calendar + } + + private fun calculatePositionFromDate(date: Calendar): Int { + val today = Calendar.getInstance() + val currentMonthPosition = Int.MAX_VALUE / 2 + val differenceInMonths = (date.get(Calendar.YEAR) - today.get(Calendar.YEAR)) * 12 + + (date.get(Calendar.MONTH) - today.get(Calendar.MONTH)) + return currentMonthPosition + differenceInMonths + } } diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingDialog.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingDialog.kt index 1ea7fb2..91a1f15 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingDialog.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingDialog.kt @@ -9,6 +9,7 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import com.nabi.nabi.R import com.nabi.nabi.databinding.DialogAddDiaryDoneBinding +import com.nabi.nabi.utils.LoggerUtils import com.nabi.nabi.utils.UiState import com.nabi.nabi.views.MainActivity import com.nabi.nabi.views.diary.detail.DetailDiaryFragment @@ -46,6 +47,10 @@ class EmotionLoadingDialog(private val isEdit: Boolean, private val diaryId: Int is UiState.Loading -> {} is UiState.Failure -> { showToast("일기 감정분석 실패") + (requireActivity() as MainActivity).replaceFragment( + DetailDiaryFragment(diaryId), + false + ) } is UiState.Success -> { @@ -64,7 +69,6 @@ class EmotionLoadingDialog(private val isEdit: Boolean, private val diaryId: Int } is UiState.Success -> { -// emotionLoadingDialog.dismiss() if (isEdit) { requireActivity().supportFragmentManager.popBackStack() } else { diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingViewModel.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingViewModel.kt index f10b79e..b0a289e 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingViewModel.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/EmotionLoadingViewModel.kt @@ -1,5 +1,6 @@ package com.nabi.nabi.views.diary.add +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -9,7 +10,8 @@ import com.nabi.domain.repository.DataStoreRepository import com.nabi.domain.usecase.datastore.GetAccessTokenUseCase import com.nabi.domain.usecase.emotion.AddDiaryEmotionUseCase import com.nabi.domain.usecase.emotion.GetDiaryEmotionUseCase -import com.nabi.domain.utils.EmotionStateUtils +import com.nabi.domain.extension.parseEmotionState +import com.nabi.nabi.utils.LoggerUtils import com.nabi.nabi.utils.UiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -19,7 +21,8 @@ import javax.inject.Inject class EmotionLoadingViewModel @Inject constructor( private val getDiaryEmotionUseCase: GetDiaryEmotionUseCase, private val addDiaryEmotionUseCase: AddDiaryEmotionUseCase, - private val getAccessTokenUseCase: GetAccessTokenUseCase + private val getAccessTokenUseCase: GetAccessTokenUseCase, + private val dataStoreRepository: DataStoreRepository ) : ViewModel() { private val _getEmotionState = MutableLiveData>(UiState.Loading) @@ -47,10 +50,9 @@ class EmotionLoadingViewModel @Inject constructor( _addEmotionState.value = UiState.Loading viewModelScope.launch { - val accessToken = getAccessTokenUseCase.invoke().getOrNull().orEmpty() - val emotion = EmotionStateUtils.parseEmotionState(emotionState) + val accessToken = dataStoreRepository.getAccessToken().getOrNull().orEmpty() - addDiaryEmotionUseCase(accessToken, diaryId, emotion) + addDiaryEmotionUseCase(accessToken, diaryId, emotionState) .onSuccess { _addEmotionState.value = UiState.Success(it) }.onFailure { e -> diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/SharedDateViewModel.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/SharedDateViewModel.kt index 023c213..94676c7 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/SharedDateViewModel.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/SharedDateViewModel.kt @@ -3,16 +3,24 @@ package com.nabi.nabi.views.diary.add import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.nabi.domain.model.diary.DiarySelectInfo class SharedDateViewModel : ViewModel() { private val _selectedDate = MutableLiveData("") val selectedDate: LiveData get() = _selectedDate + private val _date = MutableLiveData() + val date: LiveData get() = _date + fun changeSelectedDate(date: String) { _selectedDate.value = date } + fun changeDateInfo(newInfo: DiarySelectInfo){ + _date.value = newInfo + } + fun clearData() { _selectedDate.value = "" } diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryFragment.kt index 29a24b1..0f77a4b 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryFragment.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryFragment.kt @@ -1,5 +1,6 @@ package com.nabi.nabi.views.diary.detail +import android.os.Bundle import android.view.View import androidx.fragment.app.viewModels import com.nabi.nabi.R @@ -26,7 +27,7 @@ class DetailDiaryFragment( super.initListener() binding.ibBack.setOnClickListener { - requireActivity().supportFragmentManager.popBackStack() + popBackStack(true) } binding.btnEdit.setOnClickListener { @@ -51,7 +52,6 @@ class DetailDiaryFragment( deleteDialog.setButtonClickListener(object : CustomDialog.OnButtonClickListener { override fun onButton1Clicked() { viewModel.deleteDiary(diaryId) - requireActivity().supportFragmentManager.popBackStack() } override fun onButton2Clicked() { @@ -130,6 +130,7 @@ class DetailDiaryFragment( is UiState.Success -> { LoggerUtils.d(it.data.message) + popBackStack(true) } } } @@ -143,4 +144,16 @@ class DetailDiaryFragment( return "${year}년 ${month}월 ${day}일" } + + private fun popBackStack(isUpdateFlag: Boolean = false) { + val fm = requireActivity().supportFragmentManager + if (isUpdateFlag) { + val result = Bundle().apply { + putBoolean("isUpdateFlag", true) + putString("date", binding.tvDiaryDate.text.toString()) + } + fm.setFragmentResult("isUpdateFlag", result) + } + fm.popBackStack() + } } \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryViewModel.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryViewModel.kt index 2725040..4d4b44d 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryViewModel.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/detail/DetailDiaryViewModel.kt @@ -6,7 +6,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nabi.domain.model.diary.DeleteDiaryMsg import com.nabi.domain.model.diary.DiaryInfo -import com.nabi.domain.repository.DataStoreRepository import com.nabi.domain.usecase.bookmark.AddBookmarkUseCase import com.nabi.domain.usecase.bookmark.DeleteBookmarkUseCase import com.nabi.domain.usecase.datastore.GetAccessTokenUseCase @@ -23,10 +22,10 @@ class DetailDiaryViewModel @Inject constructor( private val addBookmarkUseCase: AddBookmarkUseCase, private val deleteBookmarkUseCase: DeleteBookmarkUseCase, private val deleteDiaryUseCase: DeleteDiaryUseCase, - private val getAccessTokenUseCase: GetAccessTokenUseCase + private val getAccessTokenUseCase: GetAccessTokenUseCase, ) : ViewModel() { - private val _isBookmarked = MutableLiveData(false) + private val _isBookmarked = MutableLiveData(false) val isBookmarked: LiveData get() = _isBookmarked private val _diaryState = MutableLiveData>(UiState.Loading) diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/home/HomeFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/home/HomeFragment.kt index 975998f..248db0a 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/home/HomeFragment.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/home/HomeFragment.kt @@ -2,6 +2,8 @@ package com.nabi.nabi.views.home import android.annotation.SuppressLint import android.view.View +import android.widget.Toast +import androidx.activity.OnBackPressedCallback import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import com.nabi.domain.model.home.HomeInfo @@ -27,6 +29,7 @@ import dagger.hilt.android.AndroidEntryPoint class HomeFragment : BaseFragment(R.layout.fragment_home) { private lateinit var homeRvAdapter: HomeRvAdapter private val viewModel: HomeViewModel by viewModels() + private var backPressedTime: Long = 0 override fun initView() { (requireActivity() as MainActivity).setStatusBarColor(R.color.white, false) @@ -34,6 +37,7 @@ class HomeFragment : BaseFragment(R.layout.fragment_home) { binding.tvNickname.text = "$nickname 님" viewModel.registerFcmToken() viewModel.fetchData() + addOnBackPressedCallback() setDiaryRv() } @@ -163,4 +167,19 @@ class HomeFragment : BaseFragment(R.layout.fragment_home) { null } } + + private fun addOnBackPressedCallback() { + val callback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (System.currentTimeMillis() - backPressedTime >= 2000) { + backPressedTime = System.currentTimeMillis() + Toast.makeText(requireContext(), "한번 더 누르면 앱을 종료합니다.", Toast.LENGTH_SHORT) + .show() + } else if (System.currentTimeMillis() - backPressedTime < 2000) { + requireActivity().finish() + } + } + } + requireActivity().onBackPressedDispatcher.addCallback(this, callback) + } } \ No newline at end of file