diff --git a/buildSrc/src/main/java/com/gta/buildsrc/Dependencies.kt b/buildSrc/src/main/java/com/gta/buildsrc/Dependencies.kt index ea8c08f3..0df6f70c 100644 --- a/buildSrc/src/main/java/com/gta/buildsrc/Dependencies.kt +++ b/buildSrc/src/main/java/com/gta/buildsrc/Dependencies.kt @@ -11,6 +11,7 @@ object Dependencies { const val STARTUP = "1.2.0-alpha01" const val SPLASH = "1.0.0" const val DATASTORE = "1.0.0" + const val SWIPE_REFRESH_LAYOUT = "1.1.0" // KTX const val KOTLIN = "1.7.10" @@ -72,6 +73,8 @@ object Dependencies { const val CORE = "androidx.core:core-ktx:${Versions.CORE}" const val STARTUP = "androidx.startup:startup-runtime:${Versions.STARTUP}" const val SPLASH = "androidx.core:core-splashscreen:${Versions.SPLASH}" + const val DATASTORE = "androidx.datastore:datastore-preferences:${Versions.DATASTORE}" + const val SWIPE_REFRESH_LAYOUT = "androidx.swiperefreshlayout:swiperefreshlayout:${Versions.SWIPE_REFRESH_LAYOUT}" fun getAll(): ArrayList { return arrayListOf(APP_COMPAT, CORE) @@ -209,8 +212,6 @@ object Dependencies { const val INJECT = "javax.inject:javax.inject:${Versions.INJECT}" - const val DATASTORE = "androidx.datastore:datastore-preferences:${Versions.DATASTORE}" - object Test { const val EXT = "androidx.test.ext:junit:${Versions.JUNIT_EXT}" const val JUNIT = "junit:junit:${Versions.JUNIT}" @@ -240,7 +241,7 @@ object Dependencies { addAll(Room.getAll()) add(TIMBER) addAll(Retrofit.getAll()) - add(DATASTORE) + add(AndroidX.DATASTORE) add(LOGGING_INTERCEPTER) add(Paging.PAGING) } @@ -275,6 +276,7 @@ object Dependencies { add(INDICATOR) add(LOTTIE) add(Paging.PAGING) + add(AndroidX.SWIPE_REFRESH_LAYOUT) } val presentationKaptLibraries = arrayListOf().apply { diff --git a/data/src/main/java/com/gta/data/di/RepositoryModule.kt b/data/src/main/java/com/gta/data/di/RepositoryModule.kt index d23715a0..87e5e8f1 100644 --- a/data/src/main/java/com/gta/data/di/RepositoryModule.kt +++ b/data/src/main/java/com/gta/data/di/RepositoryModule.kt @@ -3,15 +3,18 @@ package com.gta.data.di import com.gta.data.repository.CarRepositoryImpl import com.gta.data.repository.MapRepositoryImpl import com.gta.data.repository.ReportRepositoryImpl +import com.gta.data.repository.TransactionRepositoryImpl import com.gta.data.repository.UserRepositoryImpl import com.gta.data.source.CarDataSource import com.gta.data.source.MapDataSource import com.gta.data.source.ReservationDataSource import com.gta.data.source.StorageDataSource +import com.gta.data.source.TransactionDataSource import com.gta.data.source.UserDataSource import com.gta.domain.repository.CarRepository import com.gta.domain.repository.MapRepository import com.gta.domain.repository.ReportRepository +import com.gta.domain.repository.TransactionRepository import com.gta.domain.repository.UserRepository import dagger.Module import dagger.Provides @@ -58,4 +61,11 @@ class RepositoryModule { @Singleton fun provideReportRepository(userDataSource: UserDataSource): ReportRepository = ReportRepositoryImpl(userDataSource) + + @Provides + @Singleton + fun provideTransactionRepository( + transactionDataSource: TransactionDataSource + ): TransactionRepository = + TransactionRepositoryImpl(transactionDataSource) } diff --git a/data/src/main/java/com/gta/data/repository/MapRepositoryImpl.kt b/data/src/main/java/com/gta/data/repository/MapRepositoryImpl.kt index d5b17ee5..d89d8bd5 100644 --- a/data/src/main/java/com/gta/data/repository/MapRepositoryImpl.kt +++ b/data/src/main/java/com/gta/data/repository/MapRepositoryImpl.kt @@ -6,7 +6,6 @@ import com.gta.domain.model.LocationInfo import com.gta.domain.model.UCMCResult import com.gta.domain.repository.MapRepository import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import timber.log.Timber import javax.inject.Inject diff --git a/data/src/main/java/com/gta/data/repository/TransactionRepositoryImpl.kt b/data/src/main/java/com/gta/data/repository/TransactionRepositoryImpl.kt new file mode 100644 index 00000000..70a948e6 --- /dev/null +++ b/data/src/main/java/com/gta/data/repository/TransactionRepositoryImpl.kt @@ -0,0 +1,17 @@ +package com.gta.data.repository + +import com.gta.data.source.TransactionDataSource +import com.gta.domain.model.SimpleReservation +import com.gta.domain.repository.TransactionRepository +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class TransactionRepositoryImpl @Inject constructor(private val transactionDataSource: TransactionDataSource) : TransactionRepository { + override suspend fun getYourCarTransactions(uid: String): List { + return transactionDataSource.getYourCarTransactions(uid).first() + } + + override suspend fun getMyCarTransactions(uid: String): List { + return transactionDataSource.getMyCarTransactions(uid).first() + } +} diff --git a/data/src/main/java/com/gta/data/source/TransactionDataSource.kt b/data/src/main/java/com/gta/data/source/TransactionDataSource.kt new file mode 100644 index 00000000..e2d36bf4 --- /dev/null +++ b/data/src/main/java/com/gta/data/source/TransactionDataSource.kt @@ -0,0 +1,43 @@ +package com.gta.data.source + +import com.google.firebase.firestore.FirebaseFirestore +import com.gta.domain.model.Reservation +import com.gta.domain.model.SimpleReservation +import com.gta.domain.model.toSimpleReservation +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import javax.inject.Inject + +class TransactionDataSource @Inject constructor(private val fireStore: FirebaseFirestore) { + + fun getYourCarTransactions(uid: String): Flow> = callbackFlow { + fireStore.collection("reservations").whereEqualTo("lenderId", uid).get().addOnCompleteListener { + if (it.isSuccessful) { + it.result.map { snapshot -> + snapshot.toObject(Reservation::class.java).toSimpleReservation(snapshot.id) + }.also { result -> + trySend(result) + } + } else { + trySend(emptyList()) + } + } + awaitClose() + } + + fun getMyCarTransactions(uid: String): Flow> = callbackFlow { + fireStore.collection("reservations").whereEqualTo("ownerId", uid).get().addOnCompleteListener { + if (it.isSuccessful) { + it.result.map { snapshot -> + snapshot.toObject(Reservation::class.java).toSimpleReservation(snapshot.id) + }.also { result -> + trySend(result) + } + } else { + trySend(emptyList()) + } + } + awaitClose() + } +} diff --git a/domain/src/main/java/com/gta/domain/model/CarDetail.kt b/domain/src/main/java/com/gta/domain/model/CarDetail.kt index a9e70675..1ddaa91f 100644 --- a/domain/src/main/java/com/gta/domain/model/CarDetail.kt +++ b/domain/src/main/java/com/gta/domain/model/CarDetail.kt @@ -17,5 +17,5 @@ data class CarDetail( val availableDate: AvailableDate = AvailableDate(), val images: List = emptyList(), val owner: UserProfile = UserProfile(), - val coordinate: Coordinate = Coordinate(), + val coordinate: Coordinate = Coordinate() ) diff --git a/domain/src/main/java/com/gta/domain/model/Reservation.kt b/domain/src/main/java/com/gta/domain/model/Reservation.kt index a6fa3452..7933e40e 100644 --- a/domain/src/main/java/com/gta/domain/model/Reservation.kt +++ b/domain/src/main/java/com/gta/domain/model/Reservation.kt @@ -17,5 +17,6 @@ data class Reservation( fun Reservation.toSimpleReservation(reservationId: String): SimpleReservation = SimpleReservation( reservationId = reservationId, carId = carId, + reservationState = state, reservationDate = reservationDate ) diff --git a/domain/src/main/java/com/gta/domain/model/SimpleReservation.kt b/domain/src/main/java/com/gta/domain/model/SimpleReservation.kt index 7313f14e..8ff70f1d 100644 --- a/domain/src/main/java/com/gta/domain/model/SimpleReservation.kt +++ b/domain/src/main/java/com/gta/domain/model/SimpleReservation.kt @@ -3,5 +3,16 @@ package com.gta.domain.model data class SimpleReservation( val reservationId: String = "", val carId: String = "", + val reservationState: Int = ReservationState.PENDING.state, val reservationDate: AvailableDate = AvailableDate() ) + +fun SimpleReservation.toTransaction(simpleCar: SimpleCar): Transaction { + return Transaction( + reservationId = reservationId, + reservationState = ReservationState.values().find { it.state == reservationState } ?: ReservationState.RENTING, + reservationDate = reservationDate, + carModel = simpleCar.model, + thumbnailImg = simpleCar.image + ) +} diff --git a/domain/src/main/java/com/gta/domain/model/Transaction.kt b/domain/src/main/java/com/gta/domain/model/Transaction.kt new file mode 100644 index 00000000..cf46e30a --- /dev/null +++ b/domain/src/main/java/com/gta/domain/model/Transaction.kt @@ -0,0 +1,9 @@ +package com.gta.domain.model + +data class Transaction( + val reservationId: String = "", + val reservationState: ReservationState = ReservationState.PENDING, + val reservationDate: AvailableDate = AvailableDate(), + val carModel: String = "", + val thumbnailImg: String = "" +) diff --git a/domain/src/main/java/com/gta/domain/repository/TransactionRepository.kt b/domain/src/main/java/com/gta/domain/repository/TransactionRepository.kt new file mode 100644 index 00000000..5b878450 --- /dev/null +++ b/domain/src/main/java/com/gta/domain/repository/TransactionRepository.kt @@ -0,0 +1,8 @@ +package com.gta.domain.repository + +import com.gta.domain.model.SimpleReservation + +interface TransactionRepository { + suspend fun getYourCarTransactions(uid: String): List + suspend fun getMyCarTransactions(uid: String): List +} diff --git a/domain/src/main/java/com/gta/domain/usecase/transaction/GetTransactionsUseCase.kt b/domain/src/main/java/com/gta/domain/usecase/transaction/GetTransactionsUseCase.kt new file mode 100644 index 00000000..18244d90 --- /dev/null +++ b/domain/src/main/java/com/gta/domain/usecase/transaction/GetTransactionsUseCase.kt @@ -0,0 +1,37 @@ +package com.gta.domain.usecase.transaction + +import com.gta.domain.model.Transaction +import com.gta.domain.model.toTransaction +import com.gta.domain.repository.CarRepository +import com.gta.domain.repository.TransactionRepository +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class GetTransactionsUseCase @Inject constructor( + private val carRepository: CarRepository, + private val transactionRepository: TransactionRepository +) { + private val tradingCondition = + { transaction: Transaction -> transaction.reservationState.state >= 0 } + private val completedCondition = + { transaction: Transaction -> transaction.reservationState.state < 0 } + + suspend operator fun invoke( + uid: String, + isLender: Boolean, + isTrading: Boolean + ): List { + val transactions = if (isLender) { + transactionRepository.getYourCarTransactions(uid) + } else { + transactionRepository.getMyCarTransactions(uid) + } + + val filterCondition = if (isTrading) tradingCondition else completedCondition + + return transactions.map { reservation -> + val simpleCar = carRepository.getSimpleCar(reservation.carId).first() + reservation.toTransaction(simpleCar) + }.filter(filterCondition) + } +} diff --git a/presentation/src/main/java/com/gta/presentation/model/EmptyModel.kt b/presentation/src/main/java/com/gta/presentation/model/EmptyModel.kt deleted file mode 100644 index e1b89bc6..00000000 --- a/presentation/src/main/java/com/gta/presentation/model/EmptyModel.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.gta.presentation.model - -class EmptyModel diff --git a/presentation/src/main/java/com/gta/presentation/model/TransactionState.kt b/presentation/src/main/java/com/gta/presentation/model/TransactionState.kt new file mode 100644 index 00000000..e61f0ad9 --- /dev/null +++ b/presentation/src/main/java/com/gta/presentation/model/TransactionState.kt @@ -0,0 +1,9 @@ +package com.gta.presentation.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +enum class TransactionState : Parcelable { + TRADING, COMPLETED +} diff --git a/presentation/src/main/java/com/gta/presentation/model/TransactionUserState.kt b/presentation/src/main/java/com/gta/presentation/model/TransactionUserState.kt new file mode 100644 index 00000000..e2785742 --- /dev/null +++ b/presentation/src/main/java/com/gta/presentation/model/TransactionUserState.kt @@ -0,0 +1,9 @@ +package com.gta.presentation.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +enum class TransactionUserState : Parcelable { + LENDER, OWNER +} diff --git a/presentation/src/main/java/com/gta/presentation/ui/BindingAdapter.kt b/presentation/src/main/java/com/gta/presentation/ui/BindingAdapter.kt index 2bf33826..bd932651 100644 --- a/presentation/src/main/java/com/gta/presentation/ui/BindingAdapter.kt +++ b/presentation/src/main/java/com/gta/presentation/ui/BindingAdapter.kt @@ -4,8 +4,11 @@ import android.widget.Button import android.widget.ImageView import android.widget.TextView import androidx.databinding.BindingAdapter +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView import com.gta.domain.model.AvailableDate import com.gta.domain.model.NotificationType +import com.gta.domain.model.ReservationState import com.gta.domain.usecase.cardetail.UseState import com.gta.presentation.R import com.gta.presentation.model.DateType @@ -51,11 +54,7 @@ fun setReservationTime(textView: TextView, selection: AvailableDate?, dateType: when (dateType) { DateType.RANGE -> { textView.text = selection?.let { - "${DateUtil.dateFormat.format(selection.start)} ~ ${ - DateUtil.dateFormat.format( - selection.end - ) - }" + "${DateUtil.dateFormat.format(selection.start)} ~ ${DateUtil.dateFormat.format(selection.end)}" } ?: textView.resources.getString(R.string.placeholder_date_range) } DateType.DAY_COUNT -> { @@ -128,3 +127,32 @@ fun setNotificationListItemBody( } } } + +@BindingAdapter("reservation_state") +fun setReservationState(textView: TextView, reservationState: ReservationState) { + textView.text = + when (reservationState) { + ReservationState.CANCEL -> { + textView.resources.getString(R.string.cancel) + } + ReservationState.PENDING -> { + textView.resources.getString(R.string.pending) + } + ReservationState.ACCEPT -> { + textView.resources.getString(R.string.accept) + } + ReservationState.RENTING -> { + textView.resources.getString(R.string.renting) + } + ReservationState.DONE -> { + textView.resources.getString(R.string.return_completed) + } + } +} + +@BindingAdapter("submitList") +fun bindSubmitList(view: RecyclerView, itemList: List?) { + view.adapter?.let { + (view.adapter as ListAdapter).submitList(itemList) + } +} diff --git a/presentation/src/main/java/com/gta/presentation/ui/cardetail/CarDetailFragment.kt b/presentation/src/main/java/com/gta/presentation/ui/cardetail/CarDetailFragment.kt index 33b2a4b8..63535adf 100644 --- a/presentation/src/main/java/com/gta/presentation/ui/cardetail/CarDetailFragment.kt +++ b/presentation/src/main/java/com/gta/presentation/ui/cardetail/CarDetailFragment.kt @@ -122,12 +122,11 @@ class CarDetailFragment : BaseFragment( } } - override fun onStop() { viewModel.stopCollect() super.onStop() } - + private fun handleErrorMessage(e: Exception) { when (e) { is FirestoreException -> { diff --git a/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditFragment.kt b/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditFragment.kt index 802bd346..e5472c4e 100644 --- a/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditFragment.kt +++ b/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditFragment.kt @@ -17,7 +17,6 @@ import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.datepicker.DateValidatorPointForward import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.snackbar.Snackbar -import com.gta.domain.model.Coordinate import com.gta.presentation.R import com.gta.presentation.databinding.FragmentCarEditBinding import com.gta.presentation.ui.base.BaseFragment diff --git a/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditViewModel.kt b/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditViewModel.kt index 1b424d75..d40f9b0d 100644 --- a/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditViewModel.kt +++ b/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditViewModel.kt @@ -43,7 +43,6 @@ class CarEditViewModel @Inject constructor( val availableDate: StateFlow get() = _availableDate - private val _location = MutableStateFlow("정보 없음") val location: StateFlow get() = _location @@ -77,7 +76,6 @@ class CarEditViewModel @Inject constructor( _location.value = carInfo.data.location coordinate = carInfo.data.coordinate } - } } diff --git a/presentation/src/main/java/com/gta/presentation/ui/map/MapViewModel.kt b/presentation/src/main/java/com/gta/presentation/ui/map/MapViewModel.kt index 4ffd3c81..aa746cf0 100644 --- a/presentation/src/main/java/com/gta/presentation/ui/map/MapViewModel.kt +++ b/presentation/src/main/java/com/gta/presentation/ui/map/MapViewModel.kt @@ -5,8 +5,8 @@ import androidx.lifecycle.viewModelScope import com.gta.domain.model.Coordinate import com.gta.domain.model.LocationInfo import com.gta.domain.model.SimpleCar -import com.gta.domain.usecase.cardetail.edit.GetCoordinateLocationUseCase import com.gta.domain.model.UCMCResult +import com.gta.domain.usecase.cardetail.edit.GetCoordinateLocationUseCase import com.gta.domain.usecase.map.GetNearCarsUseCase import com.gta.domain.usecase.map.GetSearchAddressUseCase import com.gta.presentation.util.EventFlow diff --git a/presentation/src/main/java/com/gta/presentation/ui/mypage/MyPageFragment.kt b/presentation/src/main/java/com/gta/presentation/ui/mypage/MyPageFragment.kt index 69a584eb..fddc617f 100644 --- a/presentation/src/main/java/com/gta/presentation/ui/mypage/MyPageFragment.kt +++ b/presentation/src/main/java/com/gta/presentation/ui/mypage/MyPageFragment.kt @@ -59,6 +59,12 @@ class MyPageFragment : BaseFragment( binding.btnMypageLicense.setOnClickListener { findNavController().navigate(R.id.action_myPageFragment_to_myPageLicenseFragment) } + binding.btnMypageBuyHistory.setOnClickListener { + findNavController().navigate(MyPageFragmentDirections.actionMyPageFragmentToTransactionListFragment(getString(R.string.mypage_btn_transaction_buy))) + } + binding.btnMypageSellHistory.setOnClickListener { + findNavController().navigate(MyPageFragmentDirections.actionMyPageFragmentToTransactionListFragment(getString(R.string.mypage_btn_transaction_sell))) + } } private fun initCollector() { diff --git a/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionFragment.kt b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionFragment.kt new file mode 100644 index 00000000..09c475cc --- /dev/null +++ b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionFragment.kt @@ -0,0 +1,40 @@ +package com.gta.presentation.ui.transaction + +import android.os.Bundle +import android.view.View +import androidx.navigation.fragment.navArgs +import com.google.android.material.tabs.TabLayoutMediator +import com.gta.presentation.R +import com.gta.presentation.databinding.FragmentTransactionBinding +import com.gta.presentation.model.TransactionUserState +import com.gta.presentation.ui.MainActivity +import com.gta.presentation.ui.base.BaseFragment + +class TransactionFragment : BaseFragment(R.layout.fragment_transaction) { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val args: TransactionFragmentArgs by navArgs() + (requireActivity() as MainActivity).supportActionBar?.title = args.title + + val userState = if (args.title == getString(R.string.mypage_btn_transaction_buy)) { + TransactionUserState.LENDER + } else { + TransactionUserState.OWNER + } + + binding.vpTransactionListFragment.adapter = TransactionPagerAdapter(userState, this) + setUpTabLayoutWithViewPager() + } + + private fun setUpTabLayoutWithViewPager() { + TabLayoutMediator(binding.tlTransactionListTabs, binding.vpTransactionListFragment) { tab, position -> + val tabTitle = when (position) { + 0 -> getString(R.string.trading) + else -> getString(R.string.transaction_completed) + } + tab.text = tabTitle + }.attach() + } +} diff --git a/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListAdapter.kt b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListAdapter.kt new file mode 100644 index 00000000..2ad591a8 --- /dev/null +++ b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListAdapter.kt @@ -0,0 +1,54 @@ +package com.gta.presentation.ui.transaction + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.gta.domain.model.Transaction +import com.gta.presentation.R +import com.gta.presentation.databinding.ItemTransactionBinding + +class TransactionListAdapter : ListAdapter(TransactionDiffCallback()) { + + class TransactionViewHolder( + private val binding: ItemTransactionBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(item: Transaction) { + with(binding) { + this.item = item + root.setOnClickListener { + TransactionListFragment.navigateToTransactionReservationCheck(it, item.reservationId) + } + executePendingBindings() + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder { + return TransactionViewHolder( + DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.item_transaction, + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) { + holder.bind(getItem(position)) + } +} + +private class TransactionDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Transaction, newItem: Transaction): Boolean { + return oldItem.reservationId == newItem.reservationId + } + + override fun areContentsTheSame(oldItem: Transaction, newItem: Transaction): Boolean { + return oldItem == newItem + } +} diff --git a/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListFragment.kt b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListFragment.kt new file mode 100644 index 00000000..5c4570a2 --- /dev/null +++ b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListFragment.kt @@ -0,0 +1,33 @@ +package com.gta.presentation.ui.transaction + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.navigation.Navigation +import com.gta.presentation.R +import com.gta.presentation.databinding.FragmentTransactionListBinding +import com.gta.presentation.ui.base.BaseFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class TransactionListFragment : BaseFragment(R.layout.fragment_transaction_list) { + private val viewModel: TransactionListViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.apply { + vm = viewModel + rvTransactionListTransactions.adapter = TransactionListAdapter() + srlTransactionListRefresh.setOnRefreshListener { + srlTransactionListRefresh.isRefreshing = false + viewModel.setTransactions() + } + } + } + + companion object { + fun navigateToTransactionReservationCheck(view: View, reservationId: String) { + Navigation.findNavController(view).navigate(TransactionListFragmentDirections.actionTransactionListFragmentToReservationCheckFragment(reservationId)) + } + } +} diff --git a/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListViewModel.kt b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListViewModel.kt new file mode 100644 index 00000000..2304c176 --- /dev/null +++ b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListViewModel.kt @@ -0,0 +1,44 @@ +package com.gta.presentation.ui.transaction + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.gta.domain.model.Transaction +import com.gta.domain.usecase.transaction.GetTransactionsUseCase +import com.gta.presentation.model.TransactionState +import com.gta.presentation.model.TransactionUserState +import com.gta.presentation.util.FirebaseUtil +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class TransactionListViewModel @Inject constructor( + args: SavedStateHandle, + private val getTransactionsUseCase: GetTransactionsUseCase +) : ViewModel() { + + private val userState: TransactionUserState = args.get(TransactionPagerAdapter.USER_STATE_ARG) ?: TransactionUserState.LENDER + private val transactionState: TransactionState = args.get(TransactionPagerAdapter.TRANSACTION_STATE_ARG) ?: TransactionState.TRADING + + private val _transaction = MutableStateFlow>(emptyList()) + val transaction: StateFlow> get() = _transaction + + init { + setTransactions() + } + + fun setTransactions() { + viewModelScope.launch { + _transaction.emit( + getTransactionsUseCase( + FirebaseUtil.uid, + userState == TransactionUserState.LENDER, + transactionState == TransactionState.TRADING + ) + ) + } + } +} diff --git a/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionPagerAdapter.kt b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionPagerAdapter.kt new file mode 100644 index 00000000..c3dc0bbe --- /dev/null +++ b/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionPagerAdapter.kt @@ -0,0 +1,27 @@ +package com.gta.presentation.ui.transaction + +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.gta.presentation.model.TransactionState +import com.gta.presentation.model.TransactionUserState + +class TransactionPagerAdapter(private val userState: TransactionUserState, fragment: Fragment) : FragmentStateAdapter(fragment) { + private val transactionStates = TransactionState.values() + + override fun getItemCount(): Int = transactionStates.size + + override fun createFragment(position: Int): Fragment { + val fragment = TransactionListFragment() + fragment.arguments = Bundle().apply { + putParcelable(USER_STATE_ARG, userState) + putParcelable(TRANSACTION_STATE_ARG, transactionStates[position]) + } + return fragment + } + + companion object { + const val TRANSACTION_STATE_ARG = "TRANSACTION_STATE" + const val USER_STATE_ARG = "USER_STATE" + } +} diff --git a/presentation/src/main/res/layout/fragment_transaction.xml b/presentation/src/main/res/layout/fragment_transaction.xml new file mode 100644 index 00000000..97fcdb96 --- /dev/null +++ b/presentation/src/main/res/layout/fragment_transaction.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_transaction_list.xml b/presentation/src/main/res/layout/fragment_transaction_list.xml new file mode 100644 index 00000000..0bbfc858 --- /dev/null +++ b/presentation/src/main/res/layout/fragment_transaction_list.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/item_transaction.xml b/presentation/src/main/res/layout/item_transaction.xml new file mode 100644 index 00000000..e9358ef2 --- /dev/null +++ b/presentation/src/main/res/layout/item_transaction.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/navigation/nav_main.xml b/presentation/src/main/res/navigation/nav_main.xml index 99cf68b6..e5b38266 100644 --- a/presentation/src/main/res/navigation/nav_main.xml +++ b/presentation/src/main/res/navigation/nav_main.xml @@ -56,6 +56,9 @@ android:id="@+id/action_myPageFragment_to_myPageLicenseFragment" app:destination="@id/myPageLicenseFragment" app:popUpTo="@id/myPageFragment" /> + + + + + + + + + diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 54768623..ac2d2f8a 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -35,7 +35,6 @@ 변경사항을 업로드 중입니다.\n잠시만 기다려 주세요.. - 등록된 차량을\n주차할 장소를 선택해주세요 위치 선택하기 선택한 위치가 이 위치가 맞나요?\n선택한 곳의 장소명을 확인해주세요 @@ -187,6 +186,13 @@ %d분 거래 내역이 존재하지 않습니다. + 취소 + 보류중 + 허락 + 대여중 + 반납 완료 + 거래중 + 거래 완료 알 수 없는 오류가 발생헀어요. 사용자 정보를 찾을 수 없어요.