diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4f128007..c4536515 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -139,6 +139,11 @@ android:exported="false" android:screenOrientation="portrait" /> + + @@ -50,4 +52,10 @@ interface TodoDataSource { tripId : Long ): BaseResponse + suspend fun patchTodoData( + tripId: Long, + todoId: Long, + request: TodoChangeRequestDto + ): NonDataBaseResponse + } diff --git a/data/src/main/java/com/going/data/datasourceImpl/TodoDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/TodoDataSourceImpl.kt index d6e78607..dd60993d 100644 --- a/data/src/main/java/com/going/data/datasourceImpl/TodoDataSourceImpl.kt +++ b/data/src/main/java/com/going/data/datasourceImpl/TodoDataSourceImpl.kt @@ -3,6 +3,7 @@ package com.going.data.datasourceImpl import com.going.data.datasource.TodoDataSource import com.going.data.dto.BaseResponse import com.going.data.dto.NonDataBaseResponse +import com.going.data.dto.request.TodoChangeRequestDto import com.going.data.dto.request.TodoCreateRequestDto import com.going.data.dto.response.CheckFriendsResponseDto import com.going.data.dto.response.MyTripInfoResponseDto @@ -35,9 +36,10 @@ class TodoDataSourceImpl @Inject constructor( todoService.deleteTodo(todoId) override suspend fun getTodoDetailData( + tripId: Long, todoId: Long ): BaseResponse = - todoService.getTodoDetail(todoId) + todoService.getTodoDetail(tripId, todoId) override suspend fun getMyTripInfo( tripId: Long @@ -64,4 +66,11 @@ class TodoDataSourceImpl @Inject constructor( ): BaseResponse = todoService.getFriendsList(tripId) + override suspend fun patchTodoData( + tripId: Long, + todoId: Long, + request: TodoChangeRequestDto + ): NonDataBaseResponse = + todoService.patchTodo(tripId, todoId, request) + } diff --git a/data/src/main/java/com/going/data/dto/request/TodoChangeRequestDto.kt b/data/src/main/java/com/going/data/dto/request/TodoChangeRequestDto.kt new file mode 100644 index 00000000..c70c04c6 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/request/TodoChangeRequestDto.kt @@ -0,0 +1,22 @@ +package com.going.data.dto.request + +import com.going.domain.entity.request.TodoChangeRequestModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TodoChangeRequestDto( + @SerialName("title") + val title: String, + @SerialName("endDate") + val endDate: String, + @SerialName("allocators") + val allocators: List, + @SerialName("memo") + val memo: String?, + @SerialName("secret") + val secret: Boolean +) + +fun TodoChangeRequestModel.toTodoChangeRequestDto(): TodoChangeRequestDto = + TodoChangeRequestDto(title, endDate, allocators, memo, secret) \ No newline at end of file diff --git a/data/src/main/java/com/going/data/dto/response/TodoAllocatorResponseDto.kt b/data/src/main/java/com/going/data/dto/response/TodoAllocatorResponseDto.kt index 378227fa..d3a17c81 100644 --- a/data/src/main/java/com/going/data/dto/response/TodoAllocatorResponseDto.kt +++ b/data/src/main/java/com/going/data/dto/response/TodoAllocatorResponseDto.kt @@ -6,11 +6,15 @@ import kotlinx.serialization.Serializable @Serializable data class TodoAllocatorResponseDto( + @SerialName("participantId") + val participantId: Long, @SerialName("name") val name: String, @SerialName("isOwner") - val isOwner: Boolean + val isOwner: Boolean, + @SerialName("isAllocated") + val isAllocated: Boolean ) { fun toTodoAllocatorModel() = - TodoAllocatorModel(name, isOwner) + TodoAllocatorModel(participantId, name, isOwner, isAllocated) } \ No newline at end of file diff --git a/data/src/main/java/com/going/data/dto/response/TodoListAllocatorResponseDto.kt b/data/src/main/java/com/going/data/dto/response/TodoListAllocatorResponseDto.kt new file mode 100644 index 00000000..1b9d1c7b --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/TodoListAllocatorResponseDto.kt @@ -0,0 +1,16 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.TodoListAllocatorModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TodoListAllocatorResponseDto( + @SerialName("name") + val name: String, + @SerialName("isOwner") + val isOwner: Boolean +) { + fun toTodoListAllocatorModel() = + TodoListAllocatorModel(name, isOwner) +} \ No newline at end of file diff --git a/data/src/main/java/com/going/data/dto/response/TodoResponseDto.kt b/data/src/main/java/com/going/data/dto/response/TodoResponseDto.kt index a3d921ab..08f31aa5 100644 --- a/data/src/main/java/com/going/data/dto/response/TodoResponseDto.kt +++ b/data/src/main/java/com/going/data/dto/response/TodoResponseDto.kt @@ -1,6 +1,5 @@ package com.going.data.dto.response -import com.going.domain.entity.response.TodoAllocatorModel import com.going.domain.entity.response.TodoModel import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -14,10 +13,10 @@ data class TodoResponseDto( @SerialName("endDate") val endDate: String, @SerialName("allocators") - val allocators: List, + val allocators: List, @SerialName("secret") val secret: Boolean ) { fun toTodoModel() = - TodoModel(todoId, title, endDate, allocators.map { it.toTodoAllocatorModel() }, secret) + TodoModel(todoId, title, endDate, allocators.map { it.toTodoListAllocatorModel() }, secret) } diff --git a/data/src/main/java/com/going/data/repositoryImpl/TodoRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/TodoRepositoryImpl.kt index d7e3b77f..f1d2db98 100644 --- a/data/src/main/java/com/going/data/repositoryImpl/TodoRepositoryImpl.kt +++ b/data/src/main/java/com/going/data/repositoryImpl/TodoRepositoryImpl.kt @@ -1,7 +1,9 @@ package com.going.data.repositoryImpl import com.going.data.datasource.TodoDataSource +import com.going.data.dto.request.toTodoChangeRequestDto import com.going.data.dto.request.toTodoCreateRequestDto +import com.going.domain.entity.request.TodoChangeRequestModel import com.going.domain.entity.request.TodoCreateRequestModel import com.going.domain.entity.response.CheckFriendsModel import com.going.domain.entity.response.MyTripInfoModel @@ -40,10 +42,11 @@ class TodoRepositoryImpl @Inject constructor( } override suspend fun getTodoDetail( + tripId: Long, todoId: Long ): Result = runCatching { - todoDataSource.getTodoDetailData(todoId).data.toTodoDetailModel() + todoDataSource.getTodoDetailData(tripId, todoId).data.toTodoDetailModel() } override suspend fun getMyTripInfo( @@ -81,4 +84,13 @@ class TodoRepositoryImpl @Inject constructor( todoDataSource.getFriendsList(tripId).data.toCheckFriendsModel() } + override suspend fun patchTodo( + tripId: Long, + todoId: Long, + request: TodoChangeRequestModel + ): Result = + runCatching { + todoDataSource.patchTodoData(tripId, todoId, request.toTodoChangeRequestDto()) + } + } \ No newline at end of file diff --git a/data/src/main/java/com/going/data/service/TodoService.kt b/data/src/main/java/com/going/data/service/TodoService.kt index e3fdd949..3ec68e8f 100644 --- a/data/src/main/java/com/going/data/service/TodoService.kt +++ b/data/src/main/java/com/going/data/service/TodoService.kt @@ -2,6 +2,7 @@ package com.going.data.service import com.going.data.dto.BaseResponse import com.going.data.dto.NonDataBaseResponse +import com.going.data.dto.request.TodoChangeRequestDto import com.going.data.dto.request.TodoCreateRequestDto import com.going.data.dto.response.CheckFriendsResponseDto import com.going.data.dto.response.MyTripInfoResponseDto @@ -11,6 +12,7 @@ import com.going.data.dto.response.TodoResponseDto import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.PATCH import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -35,8 +37,9 @@ interface TodoService { @Path("todoId") todoId: Long ): NonDataBaseResponse - @GET("api/trips/todos/{todoId}") + @GET("api/trips/{tripId}/todos/{todoId}") suspend fun getTodoDetail( + @Path("tripId") tripId: Long, @Path("todoId") todoId: Long ): BaseResponse @@ -65,4 +68,11 @@ interface TodoService { @Path("tripId") tripId: Long ): BaseResponse + @PATCH("api/trips/{tripId}/todos/{todoId}") + suspend fun patchTodo( + @Path("tripId") tripId: Long, + @Path("todoId") todoId: Long, + @Body request: TodoChangeRequestDto + ): NonDataBaseResponse + } diff --git a/domain/src/main/kotlin/com/going/domain/entity/request/TodoChangeRequestModel.kt b/domain/src/main/kotlin/com/going/domain/entity/request/TodoChangeRequestModel.kt new file mode 100644 index 00000000..cd52bfc6 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/request/TodoChangeRequestModel.kt @@ -0,0 +1,9 @@ +package com.going.domain.entity.request + +data class TodoChangeRequestModel( + val title: String, + val endDate: String, + val allocators: List, + val memo: String?, + val secret: Boolean +) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/TodoAllocatorModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/TodoAllocatorModel.kt index b669ac2e..5c1a0933 100644 --- a/domain/src/main/kotlin/com/going/domain/entity/response/TodoAllocatorModel.kt +++ b/domain/src/main/kotlin/com/going/domain/entity/response/TodoAllocatorModel.kt @@ -1,6 +1,8 @@ package com.going.domain.entity.response data class TodoAllocatorModel( + val participantId: Long, val name: String, - val isOwner: Boolean -) + val isOwner: Boolean, + var isAllocated: Boolean +) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/TodoListAllocatorModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/TodoListAllocatorModel.kt new file mode 100644 index 00000000..6535cfb0 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/TodoListAllocatorModel.kt @@ -0,0 +1,6 @@ +package com.going.domain.entity.response + +data class TodoListAllocatorModel( + val name: String, + val isOwner: Boolean +) diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/TodoModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/TodoModel.kt index b84bcee6..21be41c7 100644 --- a/domain/src/main/kotlin/com/going/domain/entity/response/TodoModel.kt +++ b/domain/src/main/kotlin/com/going/domain/entity/response/TodoModel.kt @@ -4,6 +4,6 @@ data class TodoModel( val todoId: Long, val title: String, val endDate: String, - val allocators: List, + val allocators: List, val secret: Boolean ) diff --git a/domain/src/main/kotlin/com/going/domain/repository/TodoRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/TodoRepository.kt index be7dfbed..080a3a4c 100644 --- a/domain/src/main/kotlin/com/going/domain/repository/TodoRepository.kt +++ b/domain/src/main/kotlin/com/going/domain/repository/TodoRepository.kt @@ -1,5 +1,6 @@ package com.going.domain.repository +import com.going.domain.entity.request.TodoChangeRequestModel import com.going.domain.entity.request.TodoCreateRequestModel import com.going.domain.entity.response.CheckFriendsModel import com.going.domain.entity.response.MyTripInfoModel @@ -25,6 +26,7 @@ interface TodoRepository { ): Result suspend fun getTodoDetail( + tripId: Long, todoId: Long ): Result @@ -47,4 +49,10 @@ interface TodoRepository { suspend fun getFriendsList( tripId: Long ): Result + + suspend fun patchTodo( + tripId: Long, + todoId: Long, + request: TodoChangeRequestModel + ): Result } \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorAdapter.kt b/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorAdapter.kt index 694a0288..befbde4b 100644 --- a/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorAdapter.kt @@ -3,13 +3,13 @@ package com.going.presentation.todo.allocator import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter -import com.going.domain.entity.response.TodoAllocatorModel +import com.going.domain.entity.response.TodoListAllocatorModel import com.going.presentation.databinding.ItemTodoNameBinding import com.going.ui.util.ItemDiffCallback class TodoAllocatorAdapter( private val isCompleted: Boolean -) : ListAdapter(diffUtil) { +) : ListAdapter(diffUtil) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoAllocatorViewHolder { val inflater by lazy { LayoutInflater.from(parent.context) } @@ -23,7 +23,7 @@ class TodoAllocatorAdapter( } companion object { - private val diffUtil = ItemDiffCallback( + private val diffUtil = ItemDiffCallback( onItemsTheSame = { old, new -> old.name == new.name }, onContentsTheSame = { old, new -> old == new }, ) diff --git a/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorViewHolder.kt index ebfa3a52..4169aa2d 100644 --- a/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/todo/allocator/TodoAllocatorViewHolder.kt @@ -1,7 +1,7 @@ package com.going.presentation.todo.allocator import androidx.recyclerview.widget.RecyclerView -import com.going.domain.entity.response.TodoAllocatorModel +import com.going.domain.entity.response.TodoListAllocatorModel import com.going.presentation.R import com.going.presentation.databinding.ItemTodoNameBinding import com.going.ui.extension.colorOf @@ -11,7 +11,7 @@ class TodoAllocatorViewHolder( private val isCompleted: Boolean ) : RecyclerView.ViewHolder(binding.root) { - fun onBind(item: TodoAllocatorModel) { + fun onBind(item: TodoListAllocatorModel) { with(binding.tvTodoName) { text = item.name when { diff --git a/presentation/src/main/java/com/going/presentation/todo/change/TodoAllocatorAdapter.kt b/presentation/src/main/java/com/going/presentation/todo/change/TodoAllocatorAdapter.kt new file mode 100644 index 00000000..7827f0f5 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/change/TodoAllocatorAdapter.kt @@ -0,0 +1,30 @@ +package com.going.presentation.todo.change + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import com.going.domain.entity.response.TodoAllocatorModel +import com.going.presentation.databinding.ItemTodoCreateNameBinding +import com.going.ui.util.ItemDiffCallback + +class TodoAllocatorAdapter( + private val itemClick: (Int) -> Unit +) : ListAdapter(diffUtil) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoAllocatorViewHolder { + val binding: ItemTodoCreateNameBinding = + ItemTodoCreateNameBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return TodoAllocatorViewHolder(binding, itemClick) + } + + override fun onBindViewHolder(holder: TodoAllocatorViewHolder, position: Int) { + holder.onBind(getItem(position), position) + } + + companion object { + private val diffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old.participantId == new.participantId }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/change/TodoAllocatorViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/change/TodoAllocatorViewHolder.kt new file mode 100644 index 00000000..524c2100 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/change/TodoAllocatorViewHolder.kt @@ -0,0 +1,48 @@ +package com.going.presentation.todo.change + +import androidx.recyclerview.widget.RecyclerView +import com.going.domain.entity.response.TodoAllocatorModel +import com.going.presentation.R +import com.going.presentation.databinding.ItemTodoCreateNameBinding +import com.going.ui.extension.colorOf + +class TodoAllocatorViewHolder( + val binding: ItemTodoCreateNameBinding, + private val itemClick: (Int) -> Unit +) : RecyclerView.ViewHolder(binding.root) { + + fun onBind(item: TodoAllocatorModel, position: Int) { + binding.run { + tvTodoName.text = item.name + tvTodoName.isSelected = item.isAllocated + + setShapeColor(item.isOwner) + setTextColor() + + layoutTodoName.setOnClickListener { + itemClick(position) + tvTodoName.isSelected = !tvTodoName.isSelected + setTextColor() + } + } + } + + private fun setShapeColor(isOwner: Boolean) { + with(binding.tvTodoName) { + if (isOwner) { + setBackgroundResource(R.drawable.sel_todo_shape_red500_fill) + } else { + setBackgroundResource(R.drawable.sel_todo_shape_gray400_fill) + } + } + } + private fun setTextColor() { + with(binding.tvTodoName) { + if (isSelected) { + setTextColor(binding.root.context.colorOf(R.color.white_000)) + } else { + setTextColor(binding.root.context.colorOf(R.color.gray_300)) + } + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/change/TodoChangeActivity.kt b/presentation/src/main/java/com/going/presentation/todo/change/TodoChangeActivity.kt new file mode 100644 index 00000000..644f43f9 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/change/TodoChangeActivity.kt @@ -0,0 +1,184 @@ +package com.going.presentation.todo.change + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.activity.viewModels +import androidx.core.view.isVisible +import androidx.core.widget.doAfterTextChanged +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.going.presentation.R +import com.going.presentation.databinding.ActivityTodoChangeBinding +import com.going.presentation.todo.create.TodoCreateActivity.Companion.MAX_MEMO_LEN +import com.going.presentation.todo.create.TodoCreateActivity.Companion.MAX_TODO_LEN +import com.going.presentation.todo.detail.TodoDetailActivity +import com.going.ui.base.BaseActivity +import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import com.going.ui.state.UiState +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class TodoChangeActivity : BaseActivity(R.layout.activity_todo_change) { + + private val viewModel by viewModels() + + private var _adapter: TodoAllocatorAdapter? = null + private val adapter + get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } + + private var todoModDateBottomSheet: TodoModDateBottomSheet? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + initViewModel() + initDateClickBtnListener() + initFinishBtnListener() + initBackBtnListener() + getTodoInfo() + observeTodoDetailState() + observePatchTodoState() + setEtTodoNameArguments() + setEtTodoMemoArguments() + observeNameTextChanged() + observeMemoTextChanged() + } + + private fun initViewModel() { + binding.vm = viewModel + } + + private fun initDateClickBtnListener() { + binding.etTodoCreateDate.setOnSingleClickListener { + todoModDateBottomSheet = TodoModDateBottomSheet() + todoModDateBottomSheet?.show(supportFragmentManager, DATE_BOTTOM_SHEET) + } + } + + private fun initFinishBtnListener() { + binding.btnTodoMemoFinish.setOnSingleClickListener { + viewModel.patchTodoToServer() + } + } + + private fun initBackBtnListener() { + binding.btnTodoCreateBack.setOnSingleClickListener { + finish() + } + } + + private fun getTodoInfo() { + viewModel.tripId = intent.getLongExtra(EXTRA_TRIP_ID, 0) + viewModel.todoId = intent.getLongExtra(EXTRA_TODO_ID, 0) + viewModel.getTodoDetailFromServer() + } + + private fun observeTodoDetailState() { + viewModel.todoDetailState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Loading -> return@onEach + + is UiState.Success -> { + with(binding) { + etTodoCreateTodo.editText.setText(viewModel.todo.value) + etTodoCreateMemo.editText.setText(viewModel.memo.value) + } + if (!state.data.secret) { + initOurTodoNameListAdapter() + return@onEach + } + setMyTodoParticipant() + } + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Empty -> return@onEach + } + }.launchIn(lifecycleScope) + } + + private fun observePatchTodoState() { + viewModel.todoPatchState.flowWithLifecycle(lifecycle).onEach { result -> + if (result) { + toast(getString(R.string.todo_change_toast_success)) + setResult(Activity.RESULT_OK) + finish() + return@onEach + } + toast(getString(R.string.todo_change_toast_failure)) + }.launchIn(lifecycleScope) + } + + private fun initOurTodoNameListAdapter() { + _adapter = TodoAllocatorAdapter { position -> + viewModel.allocatorModelList[position].also { it.isAllocated = !it.isAllocated } + viewModel.checkIsFinishAvailable() + } + binding.rvOurTodoCreatePerson.adapter = adapter + adapter.submitList(viewModel.allocatorModelList) + binding.etTodoCreateMemo.isVisible = true + } + + private fun setMyTodoParticipant() { + with(binding) { + rvOurTodoCreatePerson.visibility = View.INVISIBLE + layoutMyTodoCreatePerson.visibility = View.VISIBLE + } + } + + private fun setEtTodoNameArguments() { + with(binding.etTodoCreateTodo) { + setMaxLen(MAX_TODO_LEN) + overWarning = getString(R.string.todo_over_error) + blankWarning = getString(R.string.name_blank_error) + } + } + + private fun setEtTodoMemoArguments() { + with(binding.etTodoCreateMemo) { + setMaxLen(MAX_MEMO_LEN) + overWarning = getString(R.string.memo_over_error) + } + } + + private fun observeNameTextChanged() { + binding.etTodoCreateTodo.editText.doAfterTextChanged { text -> + viewModel.setNameState(text.toString(), binding.etTodoCreateTodo.state) + } + } + + private fun observeMemoTextChanged() { + binding.etTodoCreateMemo.editText.doAfterTextChanged { text -> + viewModel.setMemoState(text.toString(), binding.etTodoCreateMemo.state) + } + } + + override fun onDestroy() { + super.onDestroy() + _adapter = null + if (todoModDateBottomSheet?.isAdded == true) todoModDateBottomSheet?.dismiss() + } + + companion object { + private const val DATE_BOTTOM_SHEET = "DATE_BOTTOM_SHEET" + private const val EXTRA_TRIP_ID = "EXTRA_TRIP_ID" + private const val EXTRA_TODO_ID = "EXTRA_TODO_ID" + + @JvmStatic + fun createIntent( + context: Context, + tripId: Long, + todoId: Long + ): Intent = Intent(context, TodoChangeActivity::class.java).apply { + putExtra(EXTRA_TRIP_ID, tripId) + putExtra(EXTRA_TODO_ID, todoId) + } + } +} diff --git a/presentation/src/main/java/com/going/presentation/todo/change/TodoChangeViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/change/TodoChangeViewModel.kt new file mode 100644 index 00000000..10a9dc2b --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/change/TodoChangeViewModel.kt @@ -0,0 +1,118 @@ +package com.going.presentation.todo.change + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.going.domain.entity.request.TodoChangeRequestModel +import com.going.domain.entity.response.TodoAllocatorModel +import com.going.domain.entity.response.TodoDetailModel +import com.going.domain.repository.TodoRepository +import com.going.presentation.designsystem.edittext.EditTextState +import com.going.ui.state.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class TodoChangeViewModel @Inject constructor( + private val todoRepository: TodoRepository, +) : ViewModel() { + + private val _todoDetailState = MutableStateFlow>(UiState.Empty) + val todoDetailState: StateFlow> = _todoDetailState + + private val _todoPatchState = MutableSharedFlow() + val todoPatchState: SharedFlow = _todoPatchState + + val todo = MutableLiveData("") + val memo = MutableLiveData("") + val endDate = MutableLiveData("") + + private lateinit var oldTodoInfo: TodoDetailModel + var isSecret: Boolean = false + + private val isTodoAvailable = MutableLiveData(false) + private val isMemoAvailable = MutableLiveData(true) + val isFinishAvailable = MutableLiveData(false) + + var allocatorModelList: List = listOf() + + var tripId: Long = 0 + var todoId: Long = 0 + + fun setNameState(name: String, state: EditTextState) { + todo.value = name + isTodoAvailable.value = state == EditTextState.SUCCESS + checkIsFinishAvailable() + } + + fun setMemoState(memoText: String, state: EditTextState) { + memo.value = memoText + isMemoAvailable.value = state != EditTextState.OVER + checkIsFinishAvailable() + } + + fun checkIsFinishAvailable() { + isFinishAvailable.value = + (isTodoAvailable.value == true) && (isMemoAvailable.value == true) && (!endDate.value.isNullOrEmpty()) && checkIsTodoChanged() + } + + private fun checkIsTodoChanged(): Boolean = + (todo.value != oldTodoInfo.title) || (endDate.value != oldTodoInfo.endDate) || (memo.value != oldTodoInfo.memo) || checkIsListChanged() + + private fun checkIsListChanged(): Boolean { + for (i in allocatorModelList.indices) { + if (oldTodoInfo.allocators[i].isAllocated != allocatorModelList[i].isAllocated) { + return true + } + } + return false + } + + fun getTodoDetailFromServer() { + _todoDetailState.value = UiState.Loading + viewModelScope.launch { + todoRepository.getTodoDetail(tripId, todoId) + .onSuccess { response -> + todo.value = response.title + endDate.value = response.endDate + allocatorModelList = response.allocators.map { it.copy() } + memo.value = response.memo + isSecret = response.secret + oldTodoInfo = response + _todoDetailState.value = UiState.Success(response) + } + .onFailure { + _todoDetailState.value = UiState.Failure(it.message.toString()) + } + } + } + + fun patchTodoToServer() { + if (isFinishAvailable.value == false) return + viewModelScope.launch { + todoRepository.patchTodo( + tripId, + todoId, + TodoChangeRequestModel( + title = todo.value.orEmpty(), + endDate = endDate.value.orEmpty(), + allocators = allocatorModelList.filter { it.isAllocated }.map { it.participantId }, + memo = memo.value, + secret = isSecret + ) + ) + .onSuccess { + _todoPatchState.emit(true) + } + .onFailure { + _todoPatchState.emit(false) + } + } + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/change/TodoModDateBottomSheet.kt b/presentation/src/main/java/com/going/presentation/todo/change/TodoModDateBottomSheet.kt new file mode 100644 index 00000000..c611afd3 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/change/TodoModDateBottomSheet.kt @@ -0,0 +1,42 @@ +package com.going.presentation.todo.change + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.going.presentation.R +import com.going.presentation.databinding.FragmentTodoDateBottomSheetBinding +import com.going.ui.base.BaseBottomSheet +import com.going.ui.extension.setOnSingleClickListener + +class TodoModDateBottomSheet() : + BaseBottomSheet(R.layout.fragment_todo_date_bottom_sheet) { + + private val viewModel by activityViewModels() + + override fun onStart() { + super.onStart() + dialog?.window?.setBackgroundDrawableResource(R.color.transparent) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + initFinishBtnClickListener() + } + + private fun initFinishBtnClickListener() { + binding.btnCreateTripFinish.setOnSingleClickListener { + val createdMonth = String.format(TWO_DIGIT_FORMAT, binding.dpCreateTripDate.month + 1) + val createdDay = String.format(TWO_DIGIT_FORMAT, binding.dpCreateTripDate.dayOfMonth) + viewModel.endDate.value = + binding.dpCreateTripDate.year.toString() + "." + createdMonth + "." + createdDay + viewModel.checkIsFinishAvailable() + dismiss() + } + } + + companion object { + const val TWO_DIGIT_FORMAT = "%02d" + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateActivity.kt b/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateActivity.kt index b7eb7f4e..a4977be2 100644 --- a/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateActivity.kt +++ b/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateActivity.kt @@ -31,15 +31,12 @@ class TodoCreateActivity : BaseActivity(R.layout.acti private val adapter get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } - private var todoDateBottomSheet: TodoDateBottomSheet? = null - - private var isOurTodo = true + private var todoNewDateBottomSheet: TodoNewDateBottomSheet? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initViewModel() - setTodoCreateType() initDateClickBtnListener() initFinishBtnListener() initBackBtnListener() @@ -59,16 +56,18 @@ class TodoCreateActivity : BaseActivity(R.layout.acti private fun initDateClickBtnListener() { binding.etTodoCreateDate.setOnSingleClickListener { - todoDateBottomSheet = TodoDateBottomSheet() - todoDateBottomSheet?.show(supportFragmentManager, DATE_BOTTOM_SHEET) + todoNewDateBottomSheet = TodoNewDateBottomSheet() + todoNewDateBottomSheet?.show(supportFragmentManager, DATE_BOTTOM_SHEET) } } private fun initFinishBtnListener() { binding.btnTodoMemoFinish.setOnSingleClickListener { - if (isOurTodo) viewModel.participantIdList = - adapter.currentList.filter { it.isSelected }.map { it.participantId } - viewModel.postToCreateTodoFromServer() + with(viewModel) { + if (isOurTodo) participantIdList = + adapter.currentList.filter { it.isSelected }.map { it.participantId } + postToCreateTodoFromServer() + } } } @@ -81,13 +80,15 @@ class TodoCreateActivity : BaseActivity(R.layout.acti private fun getTripInfoId() { with(viewModel) { tripId = intent.getLongExtra(EXTRA_TRIP_ID, 0) - participantIdList = intent.getIntegerArrayListExtra(EXTRA_PARTICIPANT_ID)?.map { it.toLong() } ?: listOf() + participantIdList = + intent.getIntegerArrayListExtra(EXTRA_PARTICIPANT_ID)?.map { it.toLong() } + ?: listOf() } } private fun setTodoCreateType() { - isOurTodo = intent.getBooleanExtra(EXTRA_IS_OUR_TODO, true) - if (isOurTodo) { + viewModel.isOurTodo = intent.getBooleanExtra(EXTRA_IS_OUR_TODO, true) + if (viewModel.isOurTodo) { initOurTodoNameListAdapter() setOurTodoParticipantList() } else { @@ -181,7 +182,7 @@ class TodoCreateActivity : BaseActivity(R.layout.acti override fun onDestroy() { super.onDestroy() _adapter = null - if (todoDateBottomSheet?.isAdded == true) todoDateBottomSheet?.dismiss() + if (todoNewDateBottomSheet?.isAdded == true) todoNewDateBottomSheet?.dismiss() } companion object { @@ -192,8 +193,8 @@ class TodoCreateActivity : BaseActivity(R.layout.acti private const val EXTRA_NAME = "EXTRA_NAME" private const val EXTRA_RESULT = "EXTRA_RESULT" - private const val MAX_TODO_LEN = 15 - private const val MAX_MEMO_LEN = 1000 + const val MAX_TODO_LEN = 15 + const val MAX_MEMO_LEN = 1000 @JvmStatic fun createIntent( diff --git a/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateViewModel.kt index 6f8c891c..2fe8feec 100644 --- a/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/create/TodoCreateViewModel.kt @@ -36,6 +36,8 @@ class TodoCreateViewModel @Inject constructor( var tripId: Long = 0 + var isOurTodo = true + fun setNameState(name: String, state: EditTextState) { todo.value = name isTodoAvailable.value = state == EditTextState.SUCCESS @@ -50,7 +52,7 @@ class TodoCreateViewModel @Inject constructor( fun checkIsFinishAvailable() { isFinishAvailable.value = - isTodoAvailable.value == true && isMemoAvailable.value == true && !endDate.value.isNullOrEmpty() + (isTodoAvailable.value == true) && (isMemoAvailable.value == true) && (!endDate.value.isNullOrEmpty()) } fun postToCreateTodoFromServer() { @@ -63,7 +65,7 @@ class TodoCreateViewModel @Inject constructor( endDate = endDate.value.orEmpty(), allocators = participantIdList, memo = memo.value, - secret = false, + secret = !isOurTodo, ), ) .onSuccess { response -> diff --git a/presentation/src/main/java/com/going/presentation/todo/create/TodoDateBottomSheet.kt b/presentation/src/main/java/com/going/presentation/todo/create/TodoNewDateBottomSheet.kt similarity index 97% rename from presentation/src/main/java/com/going/presentation/todo/create/TodoDateBottomSheet.kt rename to presentation/src/main/java/com/going/presentation/todo/create/TodoNewDateBottomSheet.kt index 0743d49a..d56b80f8 100644 --- a/presentation/src/main/java/com/going/presentation/todo/create/TodoDateBottomSheet.kt +++ b/presentation/src/main/java/com/going/presentation/todo/create/TodoNewDateBottomSheet.kt @@ -8,7 +8,7 @@ import com.going.presentation.databinding.FragmentTodoDateBottomSheetBinding import com.going.ui.base.BaseBottomSheet import com.going.ui.extension.setOnSingleClickListener -class TodoDateBottomSheet() : +class TodoNewDateBottomSheet() : BaseBottomSheet(R.layout.fragment_todo_date_bottom_sheet) { private val viewModel by activityViewModels() diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailActivity.kt b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailActivity.kt index 51c7279b..50ed3b6f 100644 --- a/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailActivity.kt +++ b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailActivity.kt @@ -1,16 +1,18 @@ package com.going.presentation.todo.detail +import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View +import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.core.view.isVisible import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.ActivityTodoDetailBinding -import com.going.presentation.todo.create.TodoCreateActivity +import com.going.presentation.todo.change.TodoChangeActivity import com.going.ui.base.BaseActivity import com.going.ui.extension.colorOf import com.going.ui.extension.drawableOf @@ -33,9 +35,6 @@ class TodoDetailActivity : private val adapter get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } - private var todoId: Long = 0 - private var isPublic = true - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -43,9 +42,9 @@ class TodoDetailActivity : initBackBtnClickListener() initDeleteBtnClickListener() initModBtnClickListener() - getTodoId() - setDetailData() - setTodoDetailType() + getIntentData() + initAllocatorListAdapter() + resetDetailData() observeTodoDetailState() observeTodoDeleteState() } @@ -62,34 +61,42 @@ class TodoDetailActivity : private fun initDeleteBtnClickListener() { binding.btnTodoDetailDelete.setOnSingleClickListener { - viewModel.deleteTodoFromServer(todoId) + viewModel.deleteTodoFromServer() } } private fun initModBtnClickListener() { + val startForResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + finish() + } + } binding.btnTodoDetailMod.setOnSingleClickListener { - toast(getString(R.string.will_be_update)) + startForResult.launch( + TodoChangeActivity.createIntent( + this, viewModel.tripId, viewModel.todoId + ) + ) } } - private fun getTodoId() { - todoId = intent.getLongExtra(EXTRA_TODO_ID, 0) - } - - private fun setDetailData() { - viewModel.getTodoDetailFromServer(todoId) - } - - private fun setTodoDetailType() { - isPublic = intent.getBooleanExtra(EXTRA_IS_PUBLIC, true) - if (isPublic) initAllocatorListAdapter() + private fun getIntentData() { + viewModel.tripId = intent.getLongExtra(EXTRA_TRIP_ID, 0) + viewModel.todoId = intent.getLongExtra(EXTRA_TODO_ID, 0) + viewModel.isPublic = intent.getBooleanExtra(EXTRA_IS_PUBLIC, true) } private fun initAllocatorListAdapter() { - _adapter = TripAllocatorAdapter() - binding.rvOurTodoDetailPerson.adapter = adapter + if (viewModel.isPublic) { + _adapter = TripAllocatorAdapter() + binding.rvOurTodoDetailPerson.adapter = adapter + } } + private fun resetDetailData() { + viewModel.getTodoDetailFromServer() + } private fun observeTodoDetailState() { viewModel.todoDetailState.flowWithLifecycle(lifecycle).onEach { state -> @@ -97,7 +104,7 @@ class TodoDetailActivity : is UiState.Loading -> return@onEach is UiState.Success -> { - if (isPublic) { + if (viewModel.isPublic) { adapter.submitList(state.data.allocators) } else { with(binding) { @@ -105,9 +112,17 @@ class TodoDetailActivity : layoutMyTodoCreatePerson.visibility = View.VISIBLE } } - if (state.data.memo.isBlank()) { + + with(binding) { + tvTodoCreateMemoTitle.isVisible = true + etTodoCreateMemo.isVisible = true + tvTodoMemoCounter.isVisible = true + } + + if (state.data.memo.isBlank()) { with(binding) { - etTodoCreateMemo.background = drawableOf(R.drawable.shape_rect_4_gray200_line) + etTodoCreateMemo.background = + drawableOf(R.drawable.shape_rect_4_gray200_line) etTodoCreateMemo.text = stringOf(R.string.my_todo_create_tv_memo_hint) etTodoCreateMemo.setTextColor(colorOf(R.color.gray_200)) tvTodoMemoCounter.isVisible = false @@ -145,15 +160,18 @@ class TodoDetailActivity : } companion object { + private const val EXTRA_TRIP_ID = "EXTRA_TRIP_ID" private const val EXTRA_TODO_ID = "EXTRA_TODO_ID" private const val EXTRA_IS_PUBLIC = "EXTRA_IS_PUBLIC" @JvmStatic fun createIntent( context: Context, + tripId: Long, todoId: Long, isPublic: Boolean, ): Intent = Intent(context, TodoDetailActivity::class.java).apply { + putExtra(EXTRA_TRIP_ID, tripId) putExtra(EXTRA_TODO_ID, todoId) putExtra(EXTRA_IS_PUBLIC, isPublic) } diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailViewModel.kt index d9f252ad..8e5b783b 100644 --- a/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailViewModel.kt @@ -3,7 +3,6 @@ package com.going.presentation.todo.detail import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.going.domain.entity.response.TodoAllocatorModel import com.going.domain.entity.response.TodoDetailModel import com.going.domain.repository.TodoRepository import com.going.ui.state.EnumUiState @@ -29,10 +28,14 @@ class TodoDetailViewModel @Inject constructor( private val _todoDeleteState = MutableStateFlow(EnumUiState.EMPTY) val todoDeleteState: StateFlow = _todoDeleteState - fun getTodoDetailFromServer(todoId: Long) { + var tripId: Long = 0 + var todoId: Long = 0 + var isPublic: Boolean = true + + fun getTodoDetailFromServer() { _todoDetailState.value = UiState.Loading viewModelScope.launch { - todoRepository.getTodoDetail(todoId) + todoRepository.getTodoDetail(tripId, todoId) .onSuccess { response -> todo.value = response.title endDate.value = response.endDate @@ -45,7 +48,7 @@ class TodoDetailViewModel @Inject constructor( } } - fun deleteTodoFromServer(todoId: Long) { + fun deleteTodoFromServer() { _todoDeleteState.value = EnumUiState.LOADING viewModelScope.launch { todoRepository.deleteTodo(todoId) diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/TripAllocatorViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/detail/TripAllocatorViewHolder.kt index bd943e5b..20185e00 100644 --- a/presentation/src/main/java/com/going/presentation/todo/detail/TripAllocatorViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/todo/detail/TripAllocatorViewHolder.kt @@ -1,10 +1,10 @@ package com.going.presentation.todo.detail -import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.going.domain.entity.response.TodoAllocatorModel import com.going.presentation.R import com.going.presentation.databinding.ItemTodoCreateNameBinding +import com.going.ui.extension.colorOf class TripAllocatorViewHolder( val binding: ItemTodoCreateNameBinding @@ -13,12 +13,15 @@ class TripAllocatorViewHolder( fun onBind(item: TodoAllocatorModel) { binding.run { tvTodoName.text = item.name - tvTodoName.setTextColor(ContextCompat.getColor(binding.root.context, R.color.white_000)) - if (item.isOwner) { - tvTodoName.setBackgroundResource(R.drawable.shape_rect_2_red500_fill) - } else { - tvTodoName.setBackgroundResource(R.drawable.shape_rect_2_gray400_fill) + val (backgroundResource, textColor) = when { + !item.isAllocated -> Pair(R.drawable.shape_rect_2_gray300_line, R.color.gray_300) + item.isOwner -> Pair(R.drawable.shape_rect_2_red500_fill, R.color.white_000) + else -> Pair(R.drawable.shape_rect_2_gray400_fill, R.color.white_000) + } + tvTodoName.apply { + setBackgroundResource(backgroundResource) + setTextColor(binding.root.context.colorOf(textColor)) } } } diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoCompleteFragment.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoCompleteFragment.kt index 9658ef6e..75cf07c4 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoCompleteFragment.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoCompleteFragment.kt @@ -55,7 +55,7 @@ class MyTodoCompleteFragment() : }, { todoModel -> TodoDetailActivity.createIntent( - requireContext(), todoModel.todoId, !todoModel.secret + requireContext(), viewModel.tripId, todoModel.todoId, !todoModel.secret ).apply { startActivity(this) } }) binding.rvMyTodoComplete.adapter = adapter diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoUncompleteFragment.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoUncompleteFragment.kt index 3f7ed00f..2ab1a438 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoUncompleteFragment.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoUncompleteFragment.kt @@ -55,7 +55,7 @@ class MyTodoUncompleteFragment() : { }, { todoModel -> TodoDetailActivity.createIntent( - requireContext(), todoModel.todoId, !todoModel.secret + requireContext(), viewModel.tripId, todoModel.todoId, !todoModel.secret ).apply { startActivity(this) } }) binding.rvMyTodoUncomplete.adapter = adapter diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFragment.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFragment.kt index 995f7558..3098e094 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFragment.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFragment.kt @@ -17,19 +17,19 @@ import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.R import com.going.presentation.databinding.FragmentOurTodoBinding import com.going.presentation.todo.TodoActivity.Companion.EXTRA_TRIP_ID -import com.going.ui.util.RvItemDecoration -import com.going.presentation.todo.ourtodo.checkfriends.CheckFriendsActivity import com.going.presentation.todo.create.TodoCreateActivity +import com.going.presentation.todo.ourtodo.checkfriends.CheckFriendsActivity import com.going.presentation.todo.ourtodo.friendlist.OurTodoFriendAdapter import com.going.presentation.todo.ourtodo.invite.FriendInviteDialog import com.going.presentation.todo.ourtodo.todolist.OurTodoViewPagerAdapter import com.going.ui.base.BaseFragment -import com.going.ui.state.UiState import com.going.ui.extension.colorOf import com.going.ui.extension.getWindowHeight import com.going.ui.extension.setOnSingleClickListener import com.going.ui.extension.setStatusBarColor import com.going.ui.extension.toast +import com.going.ui.state.UiState +import com.going.ui.util.RvItemDecoration import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint @@ -59,12 +59,12 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment super.onViewCreated(view, savedInstanceState) initAdapter() + initTripInfoBtnClickListener() initAddTodoBtnListener() initItemDecoration() initInviteBtnListener() initBackBtnClickListener() initTripFriendBtnClickListener() - initTripInfoBtnClickListener() setTabLayout() setViewPager() setViewPagerChangeListener() @@ -81,10 +81,20 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment } private fun initAdapter() { - _adapter = OurTodoFriendAdapter() + _adapter = OurTodoFriendAdapter(::initFriendInfoListener) binding.rvOurTripFriend.adapter = adapter } + private fun initFriendInfoListener(participantId: Long) { + // TODO: 친구 아이템 클릭 시 상세정보 구현 + } + + private fun initTripInfoBtnClickListener() { + binding.btnOurTodoTripInfo.setOnSingleClickListener { + // TODO : 여행정보 화면 이동 + } + } + private fun initAddTodoBtnListener() { binding.btnOurTodoAddTodo.setOnSingleClickListener { TodoCreateActivity.createIntent( @@ -129,12 +139,6 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment } } - private fun initTripInfoBtnClickListener() { - binding.btnOurTodoTripInfo.setOnSingleClickListener { - // TODO : 여행정보 화면 이동 - } - } - private fun setOurTripInfo() { arguments?.let { viewModel.tripId = it.getLong(EXTRA_TRIP_ID) @@ -200,9 +204,10 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment val displayHeight = activity?.getWindowHeight() ?: return@addOnOffsetChangedListener val toolbarHeight = binding.toolbarOurTodo.height val appBarHeight = appBarLayout.totalScrollRange + verticalOffset - binding.layoutOurTodoEmpty.layoutParams = (binding.layoutOurTodoEmpty.layoutParams).also { - it.height = displayHeight - toolbarHeight - appBarHeight - 300 - } + binding.layoutOurTodoEmpty.layoutParams = + (binding.layoutOurTodoEmpty.layoutParams).also { + it.height = displayHeight - toolbarHeight - appBarHeight - 300 + } if (abs(verticalOffset) == appBarLayout.totalScrollRange) { setStatusBarColor(R.color.white_000) @@ -252,7 +257,8 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment private fun setTitleTextWithDay(day: Int, isComplete: Boolean) { when { day > 0 -> { - binding.tvOurTodoTitleDown.text = getString(R.string.our_todo_title_down_before).format(day) + binding.tvOurTodoTitleDown.text = + getString(R.string.our_todo_title_down_before).format(day) setDateTextColor(6, 6) } diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendAdapter.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendAdapter.kt index bc697dc5..cdd68805 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendAdapter.kt @@ -6,7 +6,9 @@ import androidx.recyclerview.widget.RecyclerView import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.databinding.ItemTodoFriendsBinding -class OurTodoFriendAdapter : RecyclerView.Adapter() { +class OurTodoFriendAdapter( + private val onClicked: (Long) -> (Unit) +) : RecyclerView.Adapter() { private var itemList = mutableListOf() @@ -14,7 +16,7 @@ class OurTodoFriendAdapter : RecyclerView.Adapter() { val inflater by lazy { LayoutInflater.from(parent.context) } val binding: ItemTodoFriendsBinding = ItemTodoFriendsBinding.inflate(inflater, parent, false) - return OurTodoFriendViewHolder(binding) + return OurTodoFriendViewHolder(binding, onClicked) } override fun onBindViewHolder(holder: OurTodoFriendViewHolder, position: Int) { diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendViewHolder.kt index cf829bbe..a2e60fb6 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/friendlist/OurTodoFriendViewHolder.kt @@ -6,8 +6,12 @@ import coil.transform.CircleCropTransformation import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.R import com.going.presentation.databinding.ItemTodoFriendsBinding +import com.going.ui.extension.setOnSingleClickListener -class OurTodoFriendViewHolder(val binding: ItemTodoFriendsBinding) : +class OurTodoFriendViewHolder( + val binding: ItemTodoFriendsBinding, + private val onClicked: (Long) -> (Unit) +) : RecyclerView.ViewHolder(binding.root) { fun onBind(item: TripParticipantModel) { @@ -24,10 +28,15 @@ class OurTodoFriendViewHolder(val binding: ItemTodoFriendsBinding) : 6 -> R.drawable.img_profile_7 else -> R.drawable.img_profile_3 } + ivTodoFriend.load(profileImage) { transformations(CircleCropTransformation()) } + root.setOnSingleClickListener { + onClicked(item.participantId) + } + } } diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoCompleteFragment.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoCompleteFragment.kt index dd0104eb..9a223414 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoCompleteFragment.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoCompleteFragment.kt @@ -50,7 +50,7 @@ class OurTodoCompleteFragment() : true ) { todoId -> TodoDetailActivity.createIntent( - requireContext(), todoId, true + requireContext(), viewModel.tripId, todoId, true ).apply { startActivity(this) } } binding.rvOurTodoComplete.adapter = adapter diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoUncompleteFragment.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoUncompleteFragment.kt index dc0b5d83..035464a1 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoUncompleteFragment.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/todolist/OurTodoUncompleteFragment.kt @@ -50,7 +50,7 @@ class OurTodoUncompleteFragment() : false ) { todoId -> TodoDetailActivity.createIntent( - requireContext(), todoId, true + requireContext(), viewModel.tripId, todoId, true ).apply { startActivity(this) } } binding.rvOurTodoUncomplete.adapter = adapter diff --git a/presentation/src/main/res/layout/activity_todo_change.xml b/presentation/src/main/res/layout/activity_todo_change.xml new file mode 100644 index 00000000..14ec84a8 --- /dev/null +++ b/presentation/src/main/res/layout/activity_todo_change.xml @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/activity_todo_detail.xml b/presentation/src/main/res/layout/activity_todo_detail.xml index 7a5931ff..1eddbbad 100644 --- a/presentation/src/main/res/layout/activity_todo_detail.xml +++ b/presentation/src/main/res/layout/activity_todo_detail.xml @@ -91,8 +91,9 @@ android:layout_marginHorizontal="24dp" android:layout_marginTop="6dp" android:background="@drawable/shape_rect_4_gray700_line" - android:paddingHorizontal="12dp" - android:paddingVertical="19dp" + android:paddingVertical="20dp" + android:paddingStart="16dp" + android:paddingEnd="30dp" android:text="@={vm.todo}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -105,6 +106,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" + android:layout_marginEnd="4dp" android:text="@{@string/counter(vm.todo.length, vm.MAX_TODO_LEN)}" android:textColor="@color/gray_400" app:layout_constraintEnd_toEndOf="@id/et_todo_detail_todo" @@ -239,9 +241,11 @@ android:layout_marginHorizontal="24dp" android:layout_marginTop="40dp" android:text="@string/my_todo_create_tv_memo" + android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/rv_our_todo_detail_person" /> + app:layout_constraintTop_toBottomOf="@id/rv_our_todo_detail_person" + tools:visibility="visible" /> + tools:text="오늘 완전 완전 맛있는 파스타를 먹었는데 완전 아주 그냥 이게 말이지" + tools:visibility="visible" /> + tools:text="0/1000" + tools:visibility="visible" /> diff --git a/presentation/src/main/res/layout/view_emoji_counter_edittext.xml b/presentation/src/main/res/layout/view_emoji_counter_edittext.xml index f92ab255..7156f150 100644 --- a/presentation/src/main/res/layout/view_emoji_counter_edittext.xml +++ b/presentation/src/main/res/layout/view_emoji_counter_edittext.xml @@ -21,11 +21,12 @@ android:layout_height="wrap_content" android:layout_marginTop="6dp" android:background="@drawable/shape_rect_4_gray200_line" - android:paddingEnd="30dp" android:gravity="top" android:includeFontPadding="false" android:inputType="textMultiLine" + android:lineSpacingMultiplier="1.2" android:paddingVertical="20dp" + android:paddingEnd="30dp" android:textAppearance="@style/TextAppearance.Doorip.Body3.Medi" android:textColorHint="@color/gray_200" app:layout_constraintEnd_toEndOf="parent" @@ -34,11 +35,11 @@ @@ -64,5 +65,4 @@ app:layout_constraintEnd_toEndOf="@id/et_emoji_counter_et_content" app:layout_constraintTop_toBottomOf="@id/et_emoji_counter_et_content" /> - diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 84342a44..08c6a002 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -112,6 +112,11 @@ 수정하기 할일을 삭제했어요 + + 할일 수정 + 할일을 수정했어요 + 할일 수정에 실패했어요 + 초대하기 초대코드를 보내 여행 친구를 추가해 보세요 @@ -237,5 +242,4 @@ 취소 여행 이름에는 공백만 입력할 수 없어요 -