diff --git a/build.gradle b/build.gradle index 9307118..b90ebc0 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { kotlinxCoroutinesVersion = "1.6.4" paging3Version = "3.1.1" navVersion = "2.5.3" - playServicesAuthVersion = "20.3.0" + playServicesAuthVersion = "20.4.0" hiltVersion = "2.44" lottieVersion = "5.2.0" activityKtxVersion = "1.6.1" diff --git a/data/build.gradle b/data/build.gradle index 661626e..e821ae1 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -44,6 +44,8 @@ dependencies { implementation platform("com.google.firebase:firebase-bom:$firebaseVersion") implementation "com.google.firebase:firebase-analytics-ktx" implementation "com.google.firebase:firebase-firestore-ktx" + implementation "com.google.firebase:firebase-auth-ktx" + implementation "com.google.android.gms:play-services-auth:$playServicesAuthVersion" // Hilt implementation "com.google.dagger:hilt-android:$hiltVersion" diff --git a/data/src/main/java/com/whyranoid/data/account/AccountDataSource.kt b/data/src/main/java/com/whyranoid/data/account/AccountDataSource.kt index 8f0f51e..ac73095 100644 --- a/data/src/main/java/com/whyranoid/data/account/AccountDataSource.kt +++ b/data/src/main/java/com/whyranoid/data/account/AccountDataSource.kt @@ -6,5 +6,8 @@ interface AccountDataSource { fun getUserNickName(): Flow fun getUserProfileImgUri(): Flow fun getUserUid(): Flow + fun getEmail(): Flow> suspend fun updateUserNickName(uid: String, newNickName: String): Result + suspend fun signOut(): Result + suspend fun withDrawal(): Result } diff --git a/data/src/main/java/com/whyranoid/data/account/AccountDataSourceImpl.kt b/data/src/main/java/com/whyranoid/data/account/AccountDataSourceImpl.kt index f6e28e4..e29bbbf 100644 --- a/data/src/main/java/com/whyranoid/data/account/AccountDataSourceImpl.kt +++ b/data/src/main/java/com/whyranoid/data/account/AccountDataSourceImpl.kt @@ -4,10 +4,13 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey +import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore +import com.whyranoid.data.account.AccountDataSourceImpl.PreferenceKeys.email import com.whyranoid.data.account.AccountDataSourceImpl.PreferenceKeys.nickName import com.whyranoid.data.account.AccountDataSourceImpl.PreferenceKeys.profileImgUri import com.whyranoid.data.account.AccountDataSourceImpl.PreferenceKeys.uid +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject @@ -16,6 +19,9 @@ class AccountDataSourceImpl @Inject constructor( private val fireBaseDb: FirebaseFirestore ) : AccountDataSource { + private val auth = FirebaseAuth.getInstance() + private val currentUser = auth.currentUser + private object PreferenceKeys { val uid = stringPreferencesKey(UID_KEY) val email = stringPreferencesKey(EMAIL_KEY) @@ -38,6 +44,15 @@ class AccountDataSourceImpl @Inject constructor( preferences[uid] ?: EMPTY_STRING } + override fun getEmail(): Flow> { + return dataStoreDb.data + .map { preferences -> + runCatching { + preferences[email] ?: EMPTY_STRING + } + } + } + override suspend fun updateUserNickName(uid: String, newNickName: String) = runCatching { // 로컬에 업데이트 dataStoreDb.edit { preferences -> @@ -51,6 +66,15 @@ class AccountDataSourceImpl @Inject constructor( newNickName } + override suspend fun signOut(): Result = runCatching { + signOut() + true + } + + override suspend fun withDrawal(): Result { + TODO("Not yet implemented") + } + companion object { private const val UID_KEY = "uid" private const val EMAIL_KEY = "email" diff --git a/data/src/main/java/com/whyranoid/data/account/AccountRepositoryImpl.kt b/data/src/main/java/com/whyranoid/data/account/AccountRepositoryImpl.kt index 25468ba..dc5321d 100644 --- a/data/src/main/java/com/whyranoid/data/account/AccountRepositoryImpl.kt +++ b/data/src/main/java/com/whyranoid/data/account/AccountRepositoryImpl.kt @@ -25,6 +25,10 @@ class AccountRepositoryImpl @Inject constructor( return accountDataSource.getUserNickName() } + override fun getEmail(): Flow> { + return accountDataSource.getEmail() + } + override suspend fun updateNickname(uid: String, newNickName: String): Result { return accountDataSource.updateUserNickName(uid, newNickName) } @@ -36,4 +40,12 @@ class AccountRepositoryImpl @Inject constructor( override suspend fun updateProfileUrl(newProfileUrl: String): Boolean { return true } + + override suspend fun signOut(): Result { + TODO("Not yet implemented") + } + + override suspend fun withDrawal(): Result { + TODO("Not yet implemented") + } } diff --git a/data/src/main/java/com/whyranoid/data/account/RunningHistoryDao.kt b/data/src/main/java/com/whyranoid/data/account/RunningHistoryDao.kt index f18dcf4..bc70033 100644 --- a/data/src/main/java/com/whyranoid/data/account/RunningHistoryDao.kt +++ b/data/src/main/java/com/whyranoid/data/account/RunningHistoryDao.kt @@ -12,6 +12,6 @@ interface RunningHistoryDao { @Insert(onConflict = OnConflictStrategy.ABORT) suspend fun addRunningHistory(runningHistory: RunningHistoryEntity) - @Query("SELECT * FROM running_history ORDER BY startedAt ASC") + @Query("SELECT * FROM running_history ORDER BY started_at ASC") fun getRunningHistory(): Flow> } diff --git a/data/src/main/java/com/whyranoid/data/account/RunningHistoryEntity.kt b/data/src/main/java/com/whyranoid/data/account/RunningHistoryEntity.kt index 0a00462..46b5c81 100644 --- a/data/src/main/java/com/whyranoid/data/account/RunningHistoryEntity.kt +++ b/data/src/main/java/com/whyranoid/data/account/RunningHistoryEntity.kt @@ -1,5 +1,6 @@ package com.whyranoid.data.account +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import com.whyranoid.domain.model.RunningHistory @@ -8,11 +9,11 @@ import com.whyranoid.domain.model.RunningHistory data class RunningHistoryEntity( @PrimaryKey val historyId: String, - val startedAt: Long, - val finishedAt: Long, - val totalRunningTime: Int, - val pace: Double, - val totalDistance: Double + @ColumnInfo(name = "started_at") val startedAt: Long, + @ColumnInfo(name = "finished_at") val finishedAt: Long, + @ColumnInfo(name = "total_running_time") val totalRunningTime: Int, + @ColumnInfo(name = "pace") val pace: Double, + @ColumnInfo(name = "total_distance") val totalDistance: Double ) fun RunningHistoryEntity.toRunningHistory(): RunningHistory { diff --git a/data/src/main/java/com/whyranoid/data/account/RunningHistoryLocalDataSourceImpl.kt b/data/src/main/java/com/whyranoid/data/account/RunningHistoryLocalDataSourceImpl.kt index eb2a3dc..ca820b1 100644 --- a/data/src/main/java/com/whyranoid/data/account/RunningHistoryLocalDataSourceImpl.kt +++ b/data/src/main/java/com/whyranoid/data/account/RunningHistoryLocalDataSourceImpl.kt @@ -6,11 +6,11 @@ import kotlinx.coroutines.flow.map import javax.inject.Inject class RunningHistoryLocalDataSourceImpl @Inject constructor( - private val roomDb: RunningHistoryLocalDataBase + private val runningHistoryDao: RunningHistoryDao ) : RunningHistoryLocalDataSource { override fun getRunningHistory(): Flow> { - return roomDb.runningHistoryDao().getRunningHistory().map { runningHistoryList -> + return runningHistoryDao.getRunningHistory().map { runningHistoryList -> runningHistoryList.map { runningHistoryEntity -> runningHistoryEntity.toRunningHistory() } diff --git a/data/src/main/java/com/whyranoid/data/di/RunningHistoryDataBaseModule.kt b/data/src/main/java/com/whyranoid/data/di/RunningHistoryDataBaseModule.kt new file mode 100644 index 0000000..a4bcd2b --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/di/RunningHistoryDataBaseModule.kt @@ -0,0 +1,32 @@ +package com.whyranoid.data.di + +import android.content.Context +import androidx.room.Room +import com.whyranoid.data.account.RunningHistoryDao +import com.whyranoid.data.account.RunningHistoryLocalDataBase +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object RunningHistoryDataBaseModule { + @Singleton + @Provides + fun provideRoomDataBase( + @ApplicationContext appContext: Context + ): RunningHistoryLocalDataBase = Room.databaseBuilder( + appContext, + RunningHistoryLocalDataBase::class.java, + "mogakrun_running_history.db" + ) + .build() + + @Singleton + @Provides + fun provideRunningHistoryDao(runningHistoryLocalDataBase: RunningHistoryLocalDataBase): RunningHistoryDao = + runningHistoryLocalDataBase.runningHistoryDao() +} diff --git a/data/src/main/java/com/whyranoid/data/di/DataBaseModule.kt b/data/src/main/java/com/whyranoid/data/di/UserDataBaseModule.kt similarity index 65% rename from data/src/main/java/com/whyranoid/data/di/DataBaseModule.kt rename to data/src/main/java/com/whyranoid/data/di/UserDataBaseModule.kt index 45800dc..685958a 100644 --- a/data/src/main/java/com/whyranoid/data/di/DataBaseModule.kt +++ b/data/src/main/java/com/whyranoid/data/di/UserDataBaseModule.kt @@ -5,8 +5,6 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStoreFile -import androidx.room.Room -import com.whyranoid.data.account.RunningHistoryLocalDataBase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -16,22 +14,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object DataBaseModule { - @Singleton - @Provides - fun provideRoomDataBase( - @ApplicationContext appContext: Context - ): RunningHistoryLocalDataBase = Room.databaseBuilder( - appContext, - RunningHistoryLocalDataBase::class.java, - "mogakrun_running_history.db" - ) - .build() -} - -@Module -@InstallIn(SingletonComponent::class) -object DataStoreModule { +object UserDataBaseModule { private const val USER_PREFERENCES = "user_preferences" diff --git a/domain/src/main/java/com/whyranoid/domain/repository/AccountRepository.kt b/domain/src/main/java/com/whyranoid/domain/repository/AccountRepository.kt index 3b0448c..f217d52 100644 --- a/domain/src/main/java/com/whyranoid/domain/repository/AccountRepository.kt +++ b/domain/src/main/java/com/whyranoid/domain/repository/AccountRepository.kt @@ -17,6 +17,9 @@ interface AccountRepository { // 데이터스토어에서 닉네임 가져오기 fun getNickname(): Flow + // 데이터스토어에서 이메일 가져오기 + fun getEmail(): Flow> + // 닉네임 수정, 서버에 먼저 보내고 성공하면 로컬에 반영 // 실패하면 실패 사용자에게 알리기 suspend fun updateNickname(uid: String, newNickName: String): Result @@ -26,4 +29,10 @@ interface AccountRepository { // 프로필 사진 서버에 업데이트 suspend fun updateProfileUrl(newProfileUrl: String): Boolean + + // 로그아웃 + suspend fun signOut(): Result + + // 회원탈퇴 + suspend fun withDrawal(): Result } diff --git a/domain/src/main/java/com/whyranoid/domain/usecase/GetEmailUseCase.kt b/domain/src/main/java/com/whyranoid/domain/usecase/GetEmailUseCase.kt new file mode 100644 index 0000000..21a90e5 --- /dev/null +++ b/domain/src/main/java/com/whyranoid/domain/usecase/GetEmailUseCase.kt @@ -0,0 +1,9 @@ +package com.whyranoid.domain.usecase + +import com.whyranoid.domain.repository.AccountRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetEmailUseCase @Inject constructor(private val accountRepository: AccountRepository) { + operator fun invoke(): Flow> = accountRepository.getEmail() +} diff --git a/domain/src/main/java/com/whyranoid/domain/usecase/GetNicknameUseCase.kt b/domain/src/main/java/com/whyranoid/domain/usecase/GetNicknameUseCase.kt index 5e60db4..d5e0603 100644 --- a/domain/src/main/java/com/whyranoid/domain/usecase/GetNicknameUseCase.kt +++ b/domain/src/main/java/com/whyranoid/domain/usecase/GetNicknameUseCase.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow import javax.inject.Inject class GetNicknameUseCase @Inject constructor(private val accountRepository: AccountRepository) { - suspend operator fun invoke(): Flow { + operator fun invoke(): Flow { return accountRepository.getNickname() } } diff --git a/domain/src/main/java/com/whyranoid/domain/usecase/SignOutUseCase.kt b/domain/src/main/java/com/whyranoid/domain/usecase/SignOutUseCase.kt new file mode 100644 index 0000000..c596a87 --- /dev/null +++ b/domain/src/main/java/com/whyranoid/domain/usecase/SignOutUseCase.kt @@ -0,0 +1,10 @@ +package com.whyranoid.domain.usecase + +import com.whyranoid.domain.repository.AccountRepository +import javax.inject.Inject + +class SignOutUseCase @Inject constructor(private val accountRepository: AccountRepository) { + suspend operator fun invoke(): Result { + return accountRepository.signOut() + } +} diff --git a/domain/src/main/java/com/whyranoid/domain/usecase/WithDrawalUseCase.kt b/domain/src/main/java/com/whyranoid/domain/usecase/WithDrawalUseCase.kt new file mode 100644 index 0000000..3ee6bc5 --- /dev/null +++ b/domain/src/main/java/com/whyranoid/domain/usecase/WithDrawalUseCase.kt @@ -0,0 +1,10 @@ +package com.whyranoid.domain.usecase + +import com.whyranoid.domain.repository.AccountRepository +import javax.inject.Inject + +class WithDrawalUseCase @Inject constructor(private val accountRepository: AccountRepository) { + suspend operator fun invoke(): Result { + return accountRepository.withDrawal() + } +} diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/RunningHistoryUiModel.kt b/presentation/src/main/java/com/whyranoid/presentation/model/RunningHistoryUiModel.kt similarity index 94% rename from presentation/src/main/java/com/whyranoid/presentation/myrun/RunningHistoryUiModel.kt rename to presentation/src/main/java/com/whyranoid/presentation/model/RunningHistoryUiModel.kt index be99724..51978db 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/myrun/RunningHistoryUiModel.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/model/RunningHistoryUiModel.kt @@ -1,4 +1,4 @@ -package com.whyranoid.presentation.myrun +package com.whyranoid.presentation.model import com.whyranoid.domain.model.RunningHistory diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/CalendarDayBinder.kt b/presentation/src/main/java/com/whyranoid/presentation/myrun/CalendarDayBinder.kt new file mode 100644 index 0000000..586a76e --- /dev/null +++ b/presentation/src/main/java/com/whyranoid/presentation/myrun/CalendarDayBinder.kt @@ -0,0 +1,85 @@ +package com.whyranoid.presentation.myrun + +import android.view.View +import androidx.core.content.ContextCompat +import com.kizitonwose.calendarview.CalendarView +import com.kizitonwose.calendarview.model.CalendarDay +import com.kizitonwose.calendarview.model.DayOwner +import com.kizitonwose.calendarview.ui.DayBinder +import com.kizitonwose.calendarview.ui.ViewContainer +import com.whyranoid.presentation.R +import com.whyranoid.presentation.databinding.ItemCalendarDayBinding +import java.time.LocalDate + +class CalendarDayBinder( + private val calendarView: CalendarView +) : DayBinder { + private var calendar: Pair = null to null + + class DayContainer( + val binding: ItemCalendarDayBinding + ) : ViewContainer(binding.root) + + override fun create(view: View): DayContainer = + DayContainer(ItemCalendarDayBinding.bind(view)) + + override fun bind(container: DayContainer, day: CalendarDay) { + val (startDate, endDate) = this.calendar + + container.binding.tvCalendarDay.text = day.date.dayOfMonth.toString() + + if (day.owner != DayOwner.THIS_MONTH) { + container.binding.tvCalendarDay.setTextColor( + ContextCompat.getColor( + calendarView.context, + R.color.gray + ) + ) + // day.day와 day.date.monthValue를 지정해서 특정 월, 일에 달렸다는 콩 표시 가능 + } else if (day.day == 10 && day.date.monthValue == 11) { + container.binding.root.background = ( + ContextCompat.getDrawable( + calendarView.context, + R.drawable.calendar_kong + ) + ) + } else { + container.binding.tvCalendarDay.setTextColor( + ContextCompat.getColor( + calendarView.context, + R.color.black + ) + ) + } + + if (isInRange(day.date)) { + container.binding.root.setBackgroundColor( + ContextCompat.getColor( + calendarView.context, + R.color.gray + ) + ) + } + + if (startDate == day.date) { + container.binding.root.background = ( + ContextCompat.getDrawable( + calendarView.context, + R.drawable.thumbnail_src_small + ) + ) + } else if (endDate == day.date) { + container.binding.root.background = ( + ContextCompat.getDrawable( + calendarView.context, + R.drawable.thumbnail_src_small + ) + ) + } + } + + private fun isInRange(date: LocalDate): Boolean { + val (startDate, endDate) = this.calendar + return startDate == date || endDate == date || (startDate != null && endDate != null && startDate < date && date < endDate) + } +} diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunFragment.kt b/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunFragment.kt index 94fc414..f6de856 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunFragment.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunFragment.kt @@ -12,6 +12,9 @@ import com.whyranoid.presentation.databinding.FragmentMyRunBinding import com.whyranoid.presentation.util.loadImage import com.whyranoid.presentation.util.repeatWhenUiStarted import dagger.hilt.android.AndroidEntryPoint +import java.time.YearMonth +import java.time.temporal.WeekFields +import java.util.Locale @AndroidEntryPoint internal class MyRunFragment : BaseFragment(R.layout.fragment_my_run) { @@ -24,6 +27,11 @@ internal class MyRunFragment : BaseFragment(R.layout.fragm private val runningHistoryAdapter = MyRunningHistoryAdapter() + private val currentMonth = YearMonth.now() + private val firstMonth = currentMonth.minusMonths(5) + private val lastMonth = currentMonth.plusMonths(5) + private val firstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -40,27 +48,41 @@ internal class MyRunFragment : BaseFragment(R.layout.fragm topAppBar.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { - R.id.my_run_setting -> - findNavController().navigate(R.id.action_myRunFragment_to_settingFragment) + R.id.my_run_setting -> { + val direction = MyRunFragmentDirections.actionMyRunFragmentToSettingFragment() + findNavController().navigate(direction) + } } true } + + calendarView.apply { + itemAnimator = null + dayBinder = CalendarDayBinder(this) + monthScrollListener = { calendarMonth -> + onMonthScrolled(calendarMonth.yearMonth) + } + // 모든 달력 범위 설정 + setup(firstMonth, lastMonth, firstDayOfWeek) + // 첫 화면에서 보일 달 설정 + scrollToMonth(currentMonth) + } } private fun observeInfo() { - repeatWhenUiStarted { + viewLifecycleOwner.repeatWhenUiStarted { viewModel.nickName.collect { nickName -> binding.tvNickName.text = nickName } } - repeatWhenUiStarted { + viewLifecycleOwner.repeatWhenUiStarted { viewModel.profileImgUri.collect { profileImgUri -> binding.ivProfileImage.loadImage(profileImgUri) } } - repeatWhenUiStarted { + viewLifecycleOwner.repeatWhenUiStarted { viewModel.runningHistoryList.collect { runningHistoryList -> runningHistoryAdapter.submitList(runningHistoryList) } @@ -77,7 +99,10 @@ internal class MyRunFragment : BaseFragment(R.layout.fragm .setPositiveButton( getString(R.string.my_run_edit_nick_name_dialog_positive) ) { dialog, _ -> - viewModel.updateNickName(viewModel.uid.value, dialogView.findViewById(R.id.et_change_nick_name).text.toString()) + viewModel.updateNickName( + viewModel.uid.value, + dialogView.findViewById(R.id.et_change_nick_name).text.toString() + ) dialog.dismiss() } .setNegativeButton( @@ -87,4 +112,12 @@ internal class MyRunFragment : BaseFragment(R.layout.fragm }.show() } } + + private fun onMonthScrolled(currentMonth: YearMonth) { + val visibleMonth = binding.calendarView.findFirstVisibleMonth() ?: return + binding.tvMonthIndicator.text = visibleMonth.yearMonth.month.toString() + if (currentMonth != visibleMonth.yearMonth) { + binding.calendarView.smoothScrollToMonth(currentMonth) + } + } } diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunViewModel.kt b/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunViewModel.kt index d53f351..8dd5489 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunViewModel.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunViewModel.kt @@ -30,8 +30,6 @@ class MyRunViewModel @Inject constructor( getProfileImgUri() } - private val EMPTY_STRING = "" - private val _uid = MutableStateFlow(EMPTY_STRING) val uid: StateFlow get() = _uid.asStateFlow() @@ -89,4 +87,8 @@ class MyRunViewModel @Inject constructor( } } } + + companion object { + private const val EMPTY_STRING = "" + } } diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunningHistoryAdapter.kt b/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunningHistoryAdapter.kt index 05c8af7..604b7fc 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunningHistoryAdapter.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunningHistoryAdapter.kt @@ -3,11 +3,13 @@ package com.whyranoid.presentation.myrun import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.whyranoid.domain.model.RunningHistory import com.whyranoid.presentation.R import com.whyranoid.presentation.databinding.ItemRunningHistoryBinding +import com.whyranoid.presentation.model.toRunningHistoryUiModel class MyRunningHistoryAdapter : ListAdapter( @@ -23,6 +25,24 @@ class MyRunningHistoryAdapter : override fun onBindViewHolder(holder: RunningHistoryViewHolder, position: Int) { holder.bind(getItem(position)) } + + companion object { + class MyRunningHistoryDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: RunningHistory, + newItem: RunningHistory + ): Boolean { + return oldItem.historyId == newItem.historyId + } + + override fun areContentsTheSame( + oldItem: RunningHistory, + newItem: RunningHistory + ): Boolean { + return oldItem == newItem + } + } + } } class RunningHistoryViewHolder(view: View) : RecyclerView.ViewHolder(view) { diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunningHistoryDiffCallback.kt b/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunningHistoryDiffCallback.kt deleted file mode 100644 index 9086b68..0000000 --- a/presentation/src/main/java/com/whyranoid/presentation/myrun/MyRunningHistoryDiffCallback.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.whyranoid.presentation.myrun - -import androidx.recyclerview.widget.DiffUtil -import com.whyranoid.domain.model.RunningHistory - -class MyRunningHistoryDiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: RunningHistory, newItem: RunningHistory): Boolean { - return oldItem.historyId == newItem.historyId - } - - override fun areContentsTheSame(oldItem: RunningHistory, newItem: RunningHistory): Boolean { - return oldItem == newItem - } -} diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/SettingFragment.kt b/presentation/src/main/java/com/whyranoid/presentation/myrun/SettingFragment.kt index 1a6eea8..78b7ba2 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/myrun/SettingFragment.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/myrun/SettingFragment.kt @@ -1,7 +1,55 @@ package com.whyranoid.presentation.myrun +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels import com.whyranoid.presentation.R import com.whyranoid.presentation.base.BaseFragment import com.whyranoid.presentation.databinding.FragmentSettingBinding +import com.whyranoid.presentation.model.UiState +import com.whyranoid.presentation.util.repeatWhenUiStarted +import dagger.hilt.android.AndroidEntryPoint -internal class SettingFragment : BaseFragment(R.layout.fragment_setting) +@AndroidEntryPoint +internal class SettingFragment : BaseFragment(R.layout.fragment_setting) { + + private val viewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + initViews() + observeState() + } + + private fun initViews() { +// binding.tvLogOut.setOnClickListener { +// viewModel.signOut() +// val intent = Intent(requireActivity(), SignInActivity::class.java) +// startActivity(intent) +// } + } + + private fun observeState() { + viewLifecycleOwner.repeatWhenUiStarted { + viewModel.emailState.collect { emailState -> + when (emailState) { + is UiState.UnInitialized -> { + // 초기화 안됨 + } + is UiState.Loading -> { + // 로딩 중 + } + is UiState.Success -> initEmailView(emailState.value) + is UiState.Failure -> { + // 실패 + } + } + } + } + } + + private fun initEmailView(email: String) { + binding.tvConnectedAccount.text = email + } +} diff --git a/presentation/src/main/java/com/whyranoid/presentation/myrun/SettingViewModel.kt b/presentation/src/main/java/com/whyranoid/presentation/myrun/SettingViewModel.kt new file mode 100644 index 0000000..81966a3 --- /dev/null +++ b/presentation/src/main/java/com/whyranoid/presentation/myrun/SettingViewModel.kt @@ -0,0 +1,48 @@ +package com.whyranoid.presentation.myrun + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.whyranoid.domain.usecase.GetEmailUseCase +import com.whyranoid.domain.usecase.SignOutUseCase +import com.whyranoid.presentation.model.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SettingViewModel @Inject constructor( + private val getEmailUseCase: GetEmailUseCase, + private val signOutUseCase: SignOutUseCase +) : ViewModel() { + + init { + getEmail() + } + + private val _emailState = MutableStateFlow>(UiState.UnInitialized) + val emailState: StateFlow> + get() = _emailState.asStateFlow() + + private fun getEmail() { + viewModelScope.launch { + _emailState.value = UiState.Loading + + getEmailUseCase().collect { emailResult -> + emailResult.onSuccess { email -> + _emailState.value = UiState.Success(email) + }.onFailure { throwable -> + _emailState.value = UiState.Failure(throwable) + } + } + } + } + + fun signOut() { + viewModelScope.launch { + signOutUseCase.invoke() + } + } +} diff --git a/presentation/src/main/res/drawable/calendar_kong.png b/presentation/src/main/res/drawable/calendar_kong.png new file mode 100644 index 0000000..3be336f Binary files /dev/null and b/presentation/src/main/res/drawable/calendar_kong.png differ diff --git a/presentation/src/main/res/layout/fragment_my_run.xml b/presentation/src/main/res/layout/fragment_my_run.xml index be4471b..6e3cb70 100644 --- a/presentation/src/main/res/layout/fragment_my_run.xml +++ b/presentation/src/main/res/layout/fragment_my_run.xml @@ -83,18 +83,31 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/tv_nick_name" /> + + + app:layout_constraintTop_toBottomOf="@id/tv_month_indicator" /> + type="com.whyranoid.presentation.model.RunningHistoryUiModel" />