diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bfe2bd49..6c6c010a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,7 +63,6 @@ android:exported="false" android:screenOrientation="portrait" /> - @@ -105,7 +104,7 @@ android:screenOrientation="portrait" /> @@ -115,7 +114,7 @@ android:screenOrientation="portrait" /> @@ -130,7 +129,7 @@ android:screenOrientation="portrait" /> diff --git a/app/src/main/java/com/going/doorip/di/DataSourceModule.kt b/app/src/main/java/com/going/doorip/di/DataSourceModule.kt index 617e26a3..64a1ae84 100644 --- a/app/src/main/java/com/going/doorip/di/DataSourceModule.kt +++ b/app/src/main/java/com/going/doorip/di/DataSourceModule.kt @@ -1,12 +1,16 @@ package com.going.doorip.di import com.going.data.datasource.AuthDataSource +import com.going.data.datasource.DashBoardDataSource +import com.going.data.datasource.EnterTripDataSource import com.going.data.datasource.MockDataSource import com.going.data.datasource.ProfileDataSource import com.going.data.datasource.SettingDataSource import com.going.data.datasource.TendencyDataSource import com.going.data.datasource.TodoDataSource import com.going.data.datasourceImpl.AuthDataSourceImpl +import com.going.data.datasourceImpl.DashBoardDataSourceImpl +import com.going.data.datasourceImpl.EnterTripDataSourceImpl import com.going.data.datasourceImpl.MockDataSourceImpl import com.going.data.datasourceImpl.ProfileDataSourceImpl import com.going.data.datasourceImpl.SettingDataSourceImpl @@ -42,6 +46,11 @@ object DataSourceModule { fun provideTodoDataSource(todoDataSourceImpl: TodoDataSourceImpl): TodoDataSource = todoDataSourceImpl + @Provides + @Singleton + fun provideDashBoardDataSource(dashBoardDataSourceImpl: DashBoardDataSourceImpl): DashBoardDataSource = + dashBoardDataSourceImpl + @Provides @Singleton fun provideTendencyDataSource(tendencyDataSourceImpl: TendencyDataSourceImpl): TendencyDataSource = @@ -51,4 +60,9 @@ object DataSourceModule { @Singleton fun provideProfileDataSource(profileDataSourceImpl: ProfileDataSourceImpl): ProfileDataSource = profileDataSourceImpl + + @Provides + @Singleton + fun provideEnterTripDataSource(entertripDataSourceImpl: EnterTripDataSourceImpl): EnterTripDataSource = + entertripDataSourceImpl } diff --git a/app/src/main/java/com/going/doorip/di/RepositoryModule.kt b/app/src/main/java/com/going/doorip/di/RepositoryModule.kt index 624a04a3..bc50fae0 100644 --- a/app/src/main/java/com/going/doorip/di/RepositoryModule.kt +++ b/app/src/main/java/com/going/doorip/di/RepositoryModule.kt @@ -1,6 +1,8 @@ package com.going.doorip.di import com.going.data.repositoryImpl.AuthRepositoryImpl +import com.going.data.repositoryImpl.DashBoardRepositoryImpl +import com.going.data.repositoryImpl.EnterTripRepositoryImpl import com.going.data.repositoryImpl.MockRepositoryImpl import com.going.data.repositoryImpl.ProfileRepositoryImpl import com.going.data.repositoryImpl.SettingRepositoryImpl @@ -8,6 +10,8 @@ import com.going.data.repositoryImpl.TendencyRepositoryImpl import com.going.data.repositoryImpl.TodoRepositoryImpl import com.going.data.repositoryImpl.TokenRepositoryImpl import com.going.domain.repository.AuthRepository +import com.going.domain.repository.DashBoardRepository +import com.going.domain.repository.EnterTripRepository import com.going.domain.repository.MockRepository import com.going.domain.repository.ProfileRepository import com.going.domain.repository.SettingRepository @@ -49,6 +53,16 @@ object RepositoryModule { fun provideTodoRepository(todoRepositoryImpl: TodoRepositoryImpl): TodoRepository = todoRepositoryImpl + @Provides + @Singleton + fun providesDashBoardRepository(dashBoardRepositoryImpl: DashBoardRepositoryImpl): DashBoardRepository = + dashBoardRepositoryImpl + + @Provides + @Singleton + fun provideEnterTripRepository(entertripRepositoryImpl: EnterTripRepositoryImpl): EnterTripRepository = + entertripRepositoryImpl + @Provides @Singleton fun provideTendencyRepository(tendencyRepositoryImpl: TendencyRepositoryImpl): TendencyRepository = diff --git a/app/src/main/java/com/going/doorip/di/ServiceModule.kt b/app/src/main/java/com/going/doorip/di/ServiceModule.kt index e09cbd06..181d95a4 100644 --- a/app/src/main/java/com/going/doorip/di/ServiceModule.kt +++ b/app/src/main/java/com/going/doorip/di/ServiceModule.kt @@ -2,6 +2,8 @@ package com.going.doorip.di import android.provider.ContactsContract.Profile import com.going.data.service.AuthService +import com.going.data.service.DashBoardService +import com.going.data.service.EnterTripService import com.going.data.service.MockService import com.going.data.service.ProfileService import com.going.data.service.SettingService @@ -38,6 +40,16 @@ object ServiceModule { fun provideTodoService(retrofit: Retrofit): TodoService = retrofit.create(TodoService::class.java) + @Provides + @Singleton + fun provideDashBoardService(retrofit: Retrofit): DashBoardService = + retrofit.create(DashBoardService::class.java) + + @Provides + @Singleton + fun provideEnterTripService(retrofit: Retrofit): EnterTripService = + retrofit.create(EnterTripService::class.java) + @Provides @Singleton fun provideTendencyService(retrofit: Retrofit): TendencyService = @@ -47,5 +59,4 @@ object ServiceModule { @Singleton fun provideProfileService(retrofit: Retrofit): ProfileService = retrofit.create(ProfileService::class.java) - } diff --git a/core-ui/src/main/java/com/going/ui/extension/EnumUiState.kt b/core-ui/src/main/java/com/going/ui/extension/EnumUiState.kt index c39fed1c..d5a401b6 100644 --- a/core-ui/src/main/java/com/going/ui/extension/EnumUiState.kt +++ b/core-ui/src/main/java/com/going/ui/extension/EnumUiState.kt @@ -1,5 +1,5 @@ package com.going.ui.extension enum class EnumUiState { - SUCCESS, FAILURE, LOADING, EMPTY + LOADING, SUCCESS, FAILURE, EMPTY } diff --git a/data/src/main/java/com/going/data/datasource/DashBoardDataSource.kt b/data/src/main/java/com/going/data/datasource/DashBoardDataSource.kt new file mode 100644 index 00000000..699a50dd --- /dev/null +++ b/data/src/main/java/com/going/data/datasource/DashBoardDataSource.kt @@ -0,0 +1,12 @@ +package com.going.data.datasource + +import com.going.data.dto.BaseResponse +import com.going.data.dto.response.DashBoardResponseDto + +interface DashBoardDataSource { + + suspend fun getTripList( + progress: String + ): BaseResponse + +} \ No newline at end of file diff --git a/data/src/main/java/com/going/data/datasource/EnterTripDataSource.kt b/data/src/main/java/com/going/data/datasource/EnterTripDataSource.kt new file mode 100644 index 00000000..8ee6ad20 --- /dev/null +++ b/data/src/main/java/com/going/data/datasource/EnterTripDataSource.kt @@ -0,0 +1,12 @@ +package com.going.data.datasource; + +import com.going.data.dto.BaseResponse +import com.going.data.dto.request.EnterTripRequestDto +import com.going.data.dto.response.EnterTripResponseDto +import retrofit2.http.Body + +interface EnterTripDataSource { + suspend fun postEnterTrip( + @Body request: EnterTripRequestDto, + ): BaseResponse +} diff --git a/data/src/main/java/com/going/data/datasource/TodoDataSource.kt b/data/src/main/java/com/going/data/datasource/TodoDataSource.kt index 06f70e39..4f07f3a2 100644 --- a/data/src/main/java/com/going/data/datasource/TodoDataSource.kt +++ b/data/src/main/java/com/going/data/datasource/TodoDataSource.kt @@ -1,6 +1,11 @@ package com.going.data.datasource import com.going.data.dto.BaseResponse +import com.going.data.dto.NonDataBaseResponse +import com.going.data.dto.request.TodoCreateRequestDto +import com.going.data.dto.response.MyTripInfoResponseDto +import com.going.data.dto.response.OurTripInfoResponseDto +import com.going.data.dto.response.TodoDetailResponseDto import com.going.data.dto.response.TodoResponseDto interface TodoDataSource { @@ -8,6 +13,28 @@ interface TodoDataSource { suspend fun getTodoListData( tripId: Long, category: String, - progress: String, + progress: String ): BaseResponse> + + suspend fun postToCreateTodoData( + tripId: Long, + request: TodoCreateRequestDto + ): NonDataBaseResponse + + suspend fun deleteTodoData( + todoId: Long + ): NonDataBaseResponse + + suspend fun getTodoDetailData( + todoId: Long + ): BaseResponse + + suspend fun getMyTripInfo( + tripId: Long + ): BaseResponse + + suspend fun getOurTripInfo( + tripId: Long + ): BaseResponse + } diff --git a/data/src/main/java/com/going/data/datasourceImpl/DashBoardDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/DashBoardDataSourceImpl.kt new file mode 100644 index 00000000..6bafedb2 --- /dev/null +++ b/data/src/main/java/com/going/data/datasourceImpl/DashBoardDataSourceImpl.kt @@ -0,0 +1,16 @@ +package com.going.data.datasourceImpl + +import com.going.data.datasource.DashBoardDataSource +import com.going.data.dto.BaseResponse +import com.going.data.dto.response.DashBoardResponseDto +import com.going.data.service.DashBoardService +import javax.inject.Inject + +class DashBoardDataSourceImpl @Inject constructor( + private val dashBoardService: DashBoardService +) : DashBoardDataSource { + + override suspend fun getTripList(progress: String): BaseResponse = + dashBoardService.getTripList(progress) + +} diff --git a/data/src/main/java/com/going/data/datasourceImpl/EnterTripDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/EnterTripDataSourceImpl.kt new file mode 100644 index 00000000..e79aefa4 --- /dev/null +++ b/data/src/main/java/com/going/data/datasourceImpl/EnterTripDataSourceImpl.kt @@ -0,0 +1,17 @@ +package com.going.data.datasourceImpl + +import com.going.data.datasource.EnterTripDataSource +import com.going.data.dto.BaseResponse +import com.going.data.dto.request.EnterTripRequestDto +import com.going.data.dto.response.EnterTripResponseDto +import com.going.data.service.EnterTripService +import javax.inject.Inject + +class EnterTripDataSourceImpl @Inject constructor( + private val enterTripService: EnterTripService, +) : EnterTripDataSource { + override suspend fun postEnterTrip( + code: EnterTripRequestDto + ): BaseResponse = + enterTripService.postEnterTrip(code) +} 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 20823011..37c8bf39 100644 --- a/data/src/main/java/com/going/data/datasourceImpl/TodoDataSourceImpl.kt +++ b/data/src/main/java/com/going/data/datasourceImpl/TodoDataSourceImpl.kt @@ -2,6 +2,11 @@ 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.TodoCreateRequestDto +import com.going.data.dto.response.MyTripInfoResponseDto +import com.going.data.dto.response.OurTripInfoResponseDto +import com.going.data.dto.response.TodoDetailResponseDto import com.going.data.dto.response.TodoResponseDto import com.going.data.service.TodoService import javax.inject.Inject @@ -17,4 +22,30 @@ class TodoDataSourceImpl @Inject constructor( ): BaseResponse> = todoService.getTodoList(tripId, category, progress) -} \ No newline at end of file + override suspend fun postToCreateTodoData( + tripId: Long, + request: TodoCreateRequestDto + ): NonDataBaseResponse = + todoService.postToCreateTodo(tripId, request) + + override suspend fun deleteTodoData( + todoId: Long + ): NonDataBaseResponse = + todoService.deleteTodo(todoId) + + override suspend fun getTodoDetailData( + todoId: Long + ): BaseResponse = + todoService.getTodoDetail(todoId) + + override suspend fun getMyTripInfo( + tripId: Long + ): BaseResponse = + todoService.getMyTripInfo(tripId) + + override suspend fun getOurTripInfo( + tripId: Long + ): BaseResponse = + todoService.getOurTripInfo(tripId) + +} diff --git a/data/src/main/java/com/going/data/dto/request/EnterTripRequestDto.kt b/data/src/main/java/com/going/data/dto/request/EnterTripRequestDto.kt new file mode 100644 index 00000000..963d3604 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/request/EnterTripRequestDto.kt @@ -0,0 +1,15 @@ +package com.going.data.dto.request + +import com.going.domain.entity.request.EnterTripRequestModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class EnterTripRequestDto( + @SerialName("code") + val code: String, +) + +fun EnterTripRequestModel.toEnterTripRequestDto(): EnterTripRequestDto = + EnterTripRequestDto(code) + diff --git a/data/src/main/java/com/going/data/dto/request/TodoCreateRequestDto.kt b/data/src/main/java/com/going/data/dto/request/TodoCreateRequestDto.kt new file mode 100644 index 00000000..63d45dcc --- /dev/null +++ b/data/src/main/java/com/going/data/dto/request/TodoCreateRequestDto.kt @@ -0,0 +1,22 @@ +package com.going.data.dto.request + +import com.going.domain.entity.request.TodoCreateRequestModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TodoCreateRequestDto( + @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 TodoCreateRequestModel.toTodoCreateRequestDto(): TodoCreateRequestDto = + TodoCreateRequestDto(title, endDate, allocators, memo, secret) \ No newline at end of file diff --git a/data/src/main/java/com/going/data/dto/response/DashBoardResponseDto.kt b/data/src/main/java/com/going/data/dto/response/DashBoardResponseDto.kt new file mode 100644 index 00000000..3dbd4a9d --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/DashBoardResponseDto.kt @@ -0,0 +1,37 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.DashBoardModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class DashBoardResponseDto( + @SerialName("name") + val name: String, + @SerialName("trips") + val trips: List + +) { + @Serializable + data class TripsResponseDto( + @SerialName("tripId") + val tripId: Long, + @SerialName("title") + val title: String, + @SerialName("startDate") + val startDate: String, + @SerialName("endDate") + val endDate: String, + @SerialName("day") + val day: Int + ) { + fun toTripsModel() = + DashBoardModel.DashBoardTripModel(tripId, title, startDate, endDate, day) + } + + fun toDashBoardModel() = + DashBoardModel(name, trips.map { + it.toTripsModel() + }) +} + diff --git a/data/src/main/java/com/going/data/dto/response/EnterTripResponseDto.kt b/data/src/main/java/com/going/data/dto/response/EnterTripResponseDto.kt new file mode 100644 index 00000000..d2d6b7ec --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/EnterTripResponseDto.kt @@ -0,0 +1,22 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.EnterTripModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class EnterTripResponseDto( + @SerialName("tripId") + val tripId: Long, + @SerialName("title") + val title: String, + @SerialName("startDate") + val startDate: String, + @SerialName("endDate") + val endDate: String, + @SerialName("day") + val day: Int, +) { + fun toEnterTripModel() = + EnterTripModel(tripId, title, startDate, endDate, day) +} diff --git a/data/src/main/java/com/going/data/dto/response/MyTripInfoResponseDto.kt b/data/src/main/java/com/going/data/dto/response/MyTripInfoResponseDto.kt new file mode 100644 index 00000000..02f98b45 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/MyTripInfoResponseDto.kt @@ -0,0 +1,16 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.MyTripInfoModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class MyTripInfoResponseDto( + @SerialName("name") + val name: String, + @SerialName("count") + val count: Int +) { + fun toMyTripInfoModel() = + MyTripInfoModel(name, count) +} \ No newline at end of file diff --git a/data/src/main/java/com/going/data/dto/response/OurTripInfoResponseDto.kt b/data/src/main/java/com/going/data/dto/response/OurTripInfoResponseDto.kt new file mode 100644 index 00000000..6c413a7f --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/OurTripInfoResponseDto.kt @@ -0,0 +1,28 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.OurTripInfoModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class OurTripInfoResponseDto( + @SerialName("title") + val title: String, + @SerialName("day") + val day: Int, + @SerialName("startDate") + val startDate: String, + @SerialName("endDate") + val endDate: String, + @SerialName("progress") + val progress: Int, + @SerialName("code") + val code: String, + @SerialName("isComplete") + val isComplete: Boolean, + @SerialName("participants") + val participants: List +) { + fun toOurTripInfoModel() : OurTripInfoModel = + OurTripInfoModel(title, day, startDate, endDate, progress, code, isComplete, participants.map { it.toTripParticipantModel() }) +} \ 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 new file mode 100644 index 00000000..378227fa --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/TodoAllocatorResponseDto.kt @@ -0,0 +1,16 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.TodoAllocatorModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TodoAllocatorResponseDto( + @SerialName("name") + val name: String, + @SerialName("isOwner") + val isOwner: Boolean +) { + fun toTodoAllocatorModel() = + TodoAllocatorModel(name, isOwner) +} \ No newline at end of file diff --git a/data/src/main/java/com/going/data/dto/response/TodoDetailResponseDto.kt b/data/src/main/java/com/going/data/dto/response/TodoDetailResponseDto.kt new file mode 100644 index 00000000..ef635cb4 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/TodoDetailResponseDto.kt @@ -0,0 +1,22 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.TodoDetailModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TodoDetailResponseDto( + @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 toTodoDetailModel(): TodoDetailModel = + TodoDetailModel(title, endDate, allocators.map { it.toTodoAllocatorModel() }, memo, secret) +} \ 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 113d4c7a..a3d921ab 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 @@ -18,18 +18,6 @@ data class TodoResponseDto( @SerialName("secret") val secret: Boolean ) { - - @Serializable - data class TodoAllocatorResponseDto( - @SerialName("name") - val name: String, - @SerialName("isOwner") - val isOwner: Boolean - ) { - fun toTodoAllocatorModel() = - TodoAllocatorModel(name, isOwner) - } - fun toTodoModel() = TodoModel(todoId, title, endDate, allocators.map { it.toTodoAllocatorModel() }, secret) } diff --git a/data/src/main/java/com/going/data/dto/response/TripParticipantResponseDto.kt b/data/src/main/java/com/going/data/dto/response/TripParticipantResponseDto.kt new file mode 100644 index 00000000..b670292f --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/TripParticipantResponseDto.kt @@ -0,0 +1,18 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.TripParticipantModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TripParticipantResponseDto( + @SerialName("participantId") + val participantId: Long, + @SerialName("name") + val name: String, + @SerialName("result") + val result: Int +) { + fun toTripParticipantModel() : TripParticipantModel = + TripParticipantModel(participantId, name, result) +} \ No newline at end of file diff --git a/data/src/main/java/com/going/data/repositoryImpl/DashBoardRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/DashBoardRepositoryImpl.kt new file mode 100644 index 00000000..17cd4168 --- /dev/null +++ b/data/src/main/java/com/going/data/repositoryImpl/DashBoardRepositoryImpl.kt @@ -0,0 +1,19 @@ +package com.going.data.repositoryImpl + +import com.going.data.datasource.DashBoardDataSource +import com.going.domain.entity.response.DashBoardModel +import com.going.domain.repository.DashBoardRepository +import javax.inject.Inject + +class DashBoardRepositoryImpl @Inject constructor( + private val dashBoardSource: DashBoardDataSource +) : DashBoardRepository { + + override suspend fun getDashBoardList( + progress: String + ): Result = + runCatching { + dashBoardSource.getTripList(progress).data.toDashBoardModel() + } + +} \ No newline at end of file diff --git a/data/src/main/java/com/going/data/repositoryImpl/EnterTripRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/EnterTripRepositoryImpl.kt new file mode 100644 index 00000000..b7259de2 --- /dev/null +++ b/data/src/main/java/com/going/data/repositoryImpl/EnterTripRepositoryImpl.kt @@ -0,0 +1,22 @@ +package com.going.data.repositoryImpl + +import com.going.data.datasource.EnterTripDataSource +import com.going.data.dto.request.toEnterTripRequestDto +import com.going.domain.entity.request.EnterTripRequestModel +import com.going.domain.entity.response.EnterTripModel +import com.going.domain.repository.EnterTripRepository +import javax.inject.Inject + +class EnterTripRepositoryImpl @Inject constructor( + private val enterTripDataSource: EnterTripDataSource, +) : EnterTripRepository { + + override suspend fun postEnterTrip( + requestEnterTripModel: EnterTripRequestModel + ): Result = + runCatching { + enterTripDataSource.postEnterTrip( + requestEnterTripModel.toEnterTripRequestDto(), + ).data.toEnterTripModel() + } +} 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 cde10882..83996e7e 100644 --- a/data/src/main/java/com/going/data/repositoryImpl/TodoRepositoryImpl.kt +++ b/data/src/main/java/com/going/data/repositoryImpl/TodoRepositoryImpl.kt @@ -1,6 +1,11 @@ package com.going.data.repositoryImpl import com.going.data.datasource.TodoDataSource +import com.going.data.dto.request.toTodoCreateRequestDto +import com.going.domain.entity.request.TodoCreateRequestModel +import com.going.domain.entity.response.MyTripInfoModel +import com.going.domain.entity.response.OurTripInfoModel +import com.going.domain.entity.response.TodoDetailModel import com.going.domain.entity.response.TodoModel import com.going.domain.repository.TodoRepository import javax.inject.Inject @@ -18,4 +23,40 @@ class TodoRepositoryImpl @Inject constructor( todoDataSource.getTodoListData(tripId, category, progress).data.map { it.toTodoModel() } } + override suspend fun postToCreateTodo( + tripId: Long, + request: TodoCreateRequestModel + ): Result = + runCatching { + todoDataSource.postToCreateTodoData(tripId, request.toTodoCreateRequestDto()) + } + + override suspend fun deleteTodo( + todoId: Long + ): Result = + runCatching { + todoDataSource.deleteTodoData(todoId) + } + + override suspend fun getTodoDetail( + todoId: Long + ): Result = + runCatching { + todoDataSource.getTodoDetailData(todoId).data.toTodoDetailModel() + } + + override suspend fun getMyTripInfo( + tripId: Long + ): Result = + runCatching { + todoDataSource.getMyTripInfo(tripId).data.toMyTripInfoModel() + } + + override suspend fun getOurTripInfo( + tripId: Long + ): Result = + runCatching { + todoDataSource.getOurTripInfo(tripId).data.toOurTripInfoModel() + } + } \ No newline at end of file diff --git a/data/src/main/java/com/going/data/service/DashBoardService.kt b/data/src/main/java/com/going/data/service/DashBoardService.kt new file mode 100644 index 00000000..52729f88 --- /dev/null +++ b/data/src/main/java/com/going/data/service/DashBoardService.kt @@ -0,0 +1,15 @@ +package com.going.data.service + +import com.going.data.dto.BaseResponse +import com.going.data.dto.response.DashBoardResponseDto +import retrofit2.http.GET +import retrofit2.http.Query + +interface DashBoardService { + + @GET("api/trips") + suspend fun getTripList( + @Query("progress") progress: String + ) : BaseResponse + +} \ No newline at end of file diff --git a/data/src/main/java/com/going/data/service/EnterTripService.kt b/data/src/main/java/com/going/data/service/EnterTripService.kt new file mode 100644 index 00000000..8a81ee14 --- /dev/null +++ b/data/src/main/java/com/going/data/service/EnterTripService.kt @@ -0,0 +1,14 @@ +package com.going.data.service + +import com.going.data.dto.BaseResponse +import com.going.data.dto.request.EnterTripRequestDto +import com.going.data.dto.response.EnterTripResponseDto +import retrofit2.http.Body +import retrofit2.http.POST + +interface EnterTripService { + @POST("/api/trips/verify") + suspend fun postEnterTrip( + @Body request: EnterTripRequestDto, + ): BaseResponse +} 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 ed7633af..b6055380 100644 --- a/data/src/main/java/com/going/data/service/TodoService.kt +++ b/data/src/main/java/com/going/data/service/TodoService.kt @@ -1,8 +1,16 @@ package com.going.data.service import com.going.data.dto.BaseResponse +import com.going.data.dto.NonDataBaseResponse +import com.going.data.dto.request.TodoCreateRequestDto +import com.going.data.dto.response.MyTripInfoResponseDto +import com.going.data.dto.response.OurTripInfoResponseDto +import com.going.data.dto.response.TodoDetailResponseDto import com.going.data.dto.response.TodoResponseDto +import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -15,4 +23,30 @@ interface TodoService { @Query("progress") progress: String ): BaseResponse> -} \ No newline at end of file + @POST("api/trips/{tripId}/todos") + suspend fun postToCreateTodo( + @Path("tripId") tripId: Long, + @Body request: TodoCreateRequestDto + ): NonDataBaseResponse + + @DELETE("api/trips/todos/{todoId}") + suspend fun deleteTodo( + @Path("todoId") todoId: Long + ): NonDataBaseResponse + + @GET("api/trips/todos/{todoId}") + suspend fun getTodoDetail( + @Path("todoId") todoId: Long + ): BaseResponse + + @GET("/api/trips/{tripId}/my") + suspend fun getMyTripInfo( + @Path("tripId") tripId: Long + ): BaseResponse + + @GET("/api/trips/{tripId}/our") + suspend fun getOurTripInfo( + @Path("tripId") tripId: Long + ): BaseResponse + +} diff --git a/domain/src/main/kotlin/com/going/domain/entity/request/EnterTripRequestModel.kt b/domain/src/main/kotlin/com/going/domain/entity/request/EnterTripRequestModel.kt new file mode 100644 index 00000000..1db065bf --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/request/EnterTripRequestModel.kt @@ -0,0 +1,5 @@ +package com.going.domain.entity.request + +data class EnterTripRequestModel( + val code: String +) diff --git a/domain/src/main/kotlin/com/going/domain/entity/request/TodoCreateRequestModel.kt b/domain/src/main/kotlin/com/going/domain/entity/request/TodoCreateRequestModel.kt new file mode 100644 index 00000000..4f1c2a0c --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/request/TodoCreateRequestModel.kt @@ -0,0 +1,9 @@ +package com.going.domain.entity.request + +data class TodoCreateRequestModel( + 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/CompletedListModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/CompletedListModel.kt deleted file mode 100644 index b0c99d80..00000000 --- a/domain/src/main/kotlin/com/going/domain/entity/response/CompletedListModel.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.going.domain.entity.response - -data class CompletedListModel( - val title: String, - val startDate: String, - val endDate: String -) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/DashBoardModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/DashBoardModel.kt new file mode 100644 index 00000000..357ad258 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/DashBoardModel.kt @@ -0,0 +1,14 @@ +package com.going.domain.entity.response + +data class DashBoardModel( + val name: String, + val trips: List +){ + data class DashBoardTripModel( + val tripId: Long, + val title: String, + val startDate: String, + val endDate: String, + val day: Int + ) +} \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/OngoingListModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/EnterTripModel.kt similarity index 73% rename from domain/src/main/kotlin/com/going/domain/entity/response/OngoingListModel.kt rename to domain/src/main/kotlin/com/going/domain/entity/response/EnterTripModel.kt index e132f47d..c7a9e56f 100644 --- a/domain/src/main/kotlin/com/going/domain/entity/response/OngoingListModel.kt +++ b/domain/src/main/kotlin/com/going/domain/entity/response/EnterTripModel.kt @@ -1,6 +1,7 @@ package com.going.domain.entity.response -data class OngoingListModel( +data class EnterTripModel( + val tripId: Long, val title: String, val startDate: String, val endDate: String, diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/MyTripInfoModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/MyTripInfoModel.kt new file mode 100644 index 00000000..88d6e620 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/MyTripInfoModel.kt @@ -0,0 +1,6 @@ +package com.going.domain.entity.response + +data class MyTripInfoModel( + val name: String, + val count: Int +) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/OurTripInfoModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/OurTripInfoModel.kt new file mode 100644 index 00000000..449f79b2 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/OurTripInfoModel.kt @@ -0,0 +1,12 @@ +package com.going.domain.entity.response + +data class OurTripInfoModel( + val title: String, + val day: Int, + val startDate: String, + val endDate: String, + val progress: Int, + val code: String, + val isComplete: Boolean, + val participants: List +) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/TodoDetailModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/TodoDetailModel.kt new file mode 100644 index 00000000..38ce8586 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/TodoDetailModel.kt @@ -0,0 +1,9 @@ +package com.going.domain.entity.response + +data class TodoDetailModel( + 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/TripParticipantModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/TripParticipantModel.kt new file mode 100644 index 00000000..f8479c0b --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/TripParticipantModel.kt @@ -0,0 +1,7 @@ +package com.going.domain.entity.response + +data class TripParticipantModel( + val participantId: Long, + val name: String, + val result: Int +) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/TripParticipantsListModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/TripParticipantsListModel.kt index 15f20c8a..2e500982 100644 --- a/domain/src/main/kotlin/com/going/domain/entity/response/TripParticipantsListModel.kt +++ b/domain/src/main/kotlin/com/going/domain/entity/response/TripParticipantsListModel.kt @@ -7,10 +7,4 @@ data class TripParticipantsListModel( val styleC: Int, val styleD: Int, val styleE: Int, -) { - data class TripParticipantModel( - val participantId: Long, - val name: String, - val result: Int - ) -} \ No newline at end of file +) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/repository/DashBoardRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/DashBoardRepository.kt new file mode 100644 index 00000000..361b3f5b --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/repository/DashBoardRepository.kt @@ -0,0 +1,9 @@ +package com.going.domain.repository + +import com.going.domain.entity.response.DashBoardModel + +interface DashBoardRepository { + suspend fun getDashBoardList( + progress : String, + ) : Result +} \ No newline at end of file diff --git a/domain/src/main/kotlin/com/going/domain/repository/EnterTripRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/EnterTripRepository.kt new file mode 100644 index 00000000..dda2b1f3 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/repository/EnterTripRepository.kt @@ -0,0 +1,10 @@ +package com.going.domain.repository + +import com.going.domain.entity.request.EnterTripRequestModel +import com.going.domain.entity.response.EnterTripModel + +interface EnterTripRepository { + suspend fun postEnterTrip( + requestEnterTripModel: EnterTripRequestModel + ): Result +} 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 397dfb9c..993084af 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,9 @@ package com.going.domain.repository +import com.going.domain.entity.request.TodoCreateRequestModel +import com.going.domain.entity.response.MyTripInfoModel +import com.going.domain.entity.response.OurTripInfoModel +import com.going.domain.entity.response.TodoDetailModel import com.going.domain.entity.response.TodoModel interface TodoRepository { @@ -10,4 +14,25 @@ interface TodoRepository { progress: String ): Result> + suspend fun postToCreateTodo( + tripId: Long, + request: TodoCreateRequestModel + ): Result + + suspend fun deleteTodo( + todoId: Long + ): Result + + suspend fun getTodoDetail( + todoId: Long + ): Result + + suspend fun getMyTripInfo( + tripId: Long + ): Result + + suspend fun getOurTripInfo( + tripId: Long + ): Result + } \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/checkfriends/CheckFriendsViewModel.kt b/presentation/src/main/java/com/going/presentation/checkfriends/CheckFriendsViewModel.kt index 7fc46905..4b1d0a54 100644 --- a/presentation/src/main/java/com/going/presentation/checkfriends/CheckFriendsViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/checkfriends/CheckFriendsViewModel.kt @@ -1,17 +1,17 @@ package com.going.presentation.checkfriends import androidx.lifecycle.ViewModel -import com.going.domain.entity.response.TripParticipantsListModel +import com.going.domain.entity.response.TripParticipantModel class CheckFriendsViewModel : ViewModel() { - val mockParticipantsList: List = listOf( - TripParticipantsListModel.TripParticipantModel(0, "일지민", 100), - TripParticipantsListModel.TripParticipantModel(1, "이지민", 100), - TripParticipantsListModel.TripParticipantModel(2, "삼지민", 100), - TripParticipantsListModel.TripParticipantModel(3, "사지민", 100), - TripParticipantsListModel.TripParticipantModel(4, "오지민", 100), - TripParticipantsListModel.TripParticipantModel(5, "육지민", 100), + val mockParticipantsList: List = listOf( + TripParticipantModel(0, "일지민", 100), + TripParticipantModel(1, "이지민", 100), + TripParticipantModel(2, "삼지민", 100), + TripParticipantModel(3, "사지민", 100), + TripParticipantModel(4, "오지민", 100), + TripParticipantModel(5, "육지민", 100), ) } \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/TripDashBoardActivity.kt b/presentation/src/main/java/com/going/presentation/dashboard/DashBoardActivity.kt similarity index 60% rename from presentation/src/main/java/com/going/presentation/tripdashboard/TripDashBoardActivity.kt rename to presentation/src/main/java/com/going/presentation/dashboard/DashBoardActivity.kt index 658a1cf3..6053cdb2 100644 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/TripDashBoardActivity.kt +++ b/presentation/src/main/java/com/going/presentation/dashboard/DashBoardActivity.kt @@ -1,21 +1,28 @@ -package com.going.presentation.tripdashboard +package com.going.presentation.dashboard import android.os.Bundle +import androidx.activity.viewModels import com.going.presentation.R +import com.going.presentation.dashboard.triplist.OngoingTripFragment import com.going.presentation.databinding.ActivityTripDashBoardBinding import com.going.ui.base.BaseActivity import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint -class TripDashBoardActivity : +@AndroidEntryPoint +class DashBoardActivity : BaseActivity(R.layout.activity_trip_dash_board) { private val tabTextList = listOf(TAB_ONGOING, TAB_COMPLETED) + private val viewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setTabLayout() setViewPager() + setTravelerName() } @@ -31,14 +38,23 @@ class TripDashBoardActivity : private fun setViewPager() { binding.vpDashboard.adapter = DashBoardViewPagerAdapter(this) + binding.vpDashboard.isUserInputEnabled = false TabLayoutMediator(binding.tabDashboard, binding.vpDashboard) { tab, pos -> tab.text = tabTextList[pos] }.attach() } + private fun setTravelerName() { + val progress = DashBoardViewModel.COMPLETED + viewModel.getTravelerNameFromServer(progress) + viewModel.name.observe(this) { travelerName -> + binding.tvDashboardTitle.text = getString(R.string.dashboard_tv_title, travelerName) + } + } + companion object { const val TAB_ONGOING = "진행중인 여행" - const val TAB_COMPLETED = "완료된 여행" + const val TAB_COMPLETED = "지나간 여행" } } \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/dashboard/DashBoardViewModel.kt b/presentation/src/main/java/com/going/presentation/dashboard/DashBoardViewModel.kt new file mode 100644 index 00000000..427939de --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/dashboard/DashBoardViewModel.kt @@ -0,0 +1,68 @@ +package com.going.presentation.dashboard + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.going.domain.entity.response.DashBoardModel +import com.going.domain.repository.DashBoardRepository +import com.going.ui.extension.UiState +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 DashBoardViewModel @Inject constructor( + private val dashBoardRepository: DashBoardRepository +) : ViewModel() { + private val _dashBoardOngoingListState = + MutableStateFlow>(UiState.Empty) + val dashBoardOngoingListState: StateFlow> get() = _dashBoardOngoingListState + + private val _dashBoardCompletedListState = + MutableStateFlow>(UiState.Empty) + + val name = MutableLiveData("") + + val dashBoardCompletedListState: StateFlow> get() = _dashBoardCompletedListState + + fun getTripListFromServer( + progress: String + ) { + val dashBoardListState = if (progress == ONGOING) { + _dashBoardOngoingListState + } else { + _dashBoardCompletedListState + } + dashBoardListState.value = UiState.Loading + viewModelScope.launch { + dashBoardRepository.getDashBoardList(progress) + .onSuccess { + dashBoardListState.value = UiState.Success(it) + } + .onFailure { + dashBoardListState.value = UiState.Failure(it.message.orEmpty()) + } + } + } + + fun getTravelerNameFromServer(progress: String) { + viewModelScope.launch { + dashBoardRepository.getDashBoardList(progress) + .onSuccess { + name.value = it.name + } + .onFailure { + name.value = null + } + } + } + + + companion object { + const val ONGOING = "incomplete" + const val COMPLETED = "complete" + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/DashBoardViewPagerAdapter.kt b/presentation/src/main/java/com/going/presentation/dashboard/DashBoardViewPagerAdapter.kt similarity index 70% rename from presentation/src/main/java/com/going/presentation/tripdashboard/DashBoardViewPagerAdapter.kt rename to presentation/src/main/java/com/going/presentation/dashboard/DashBoardViewPagerAdapter.kt index 9f121083..a0baf469 100644 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/DashBoardViewPagerAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/dashboard/DashBoardViewPagerAdapter.kt @@ -1,10 +1,10 @@ -package com.going.presentation.tripdashboard +package com.going.presentation.dashboard import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter -import com.going.presentation.tripdashboard.triplist.CompletedTripFragment -import com.going.presentation.tripdashboard.triplist.OngoingTripFragment +import com.going.presentation.dashboard.triplist.CompletedTripFragment +import com.going.presentation.dashboard.triplist.OngoingTripFragment class DashBoardViewPagerAdapter(fragment: FragmentActivity) : FragmentStateAdapter(fragment) { override fun getItemCount(): Int = 2 diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedAdapter.kt b/presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedAdapter.kt similarity index 76% rename from presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedAdapter.kt rename to presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedAdapter.kt index 333c39a9..d4af5574 100644 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedAdapter.kt @@ -1,18 +1,18 @@ -package com.going.presentation.tripdashboard.triplist +package com.going.presentation.dashboard.triplist import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter -import com.going.domain.entity.response.CompletedListModel +import com.going.domain.entity.response.DashBoardModel.DashBoardTripModel import com.going.presentation.databinding.ItemDashBoardCompletedBinding import com.going.ui.extension.ItemDiffCallback class CompletedAdapter( private val listener: OnDashBoardSelectedListener -) : ListAdapter(diffUtil) { +) : ListAdapter(diffUtil) { interface OnDashBoardSelectedListener { - fun onDashBoardSelectedListener(tripCreate: CompletedListModel) + fun onDashBoardSelectedListener(tripCreate: DashBoardTripModel) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CompletedViewHolder { @@ -30,7 +30,7 @@ class CompletedAdapter( } companion object { - private val diffUtil = ItemDiffCallback( + private val diffUtil = ItemDiffCallback( onItemsTheSame = { old, new -> old.title == new.title }, onContentsTheSame = { old, new -> old == new }, ) diff --git a/presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedTripFragment.kt b/presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedTripFragment.kt new file mode 100644 index 00000000..ecbd0419 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedTripFragment.kt @@ -0,0 +1,87 @@ +package com.going.presentation.dashboard.triplist + +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.going.domain.entity.response.DashBoardModel.DashBoardTripModel +import com.going.presentation.R +import com.going.presentation.dashboard.DashBoardViewModel +import com.going.presentation.databinding.FragmentCompletedTripBinding +import com.going.ui.base.BaseFragment +import com.going.ui.extension.UiState +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class CompletedTripFragment : + BaseFragment(R.layout.fragment_completed_trip), + CompletedAdapter.OnDashBoardSelectedListener { + + private val viewModel by activityViewModels() + + private var _adapter: CompletedAdapter? = null + private val adapter get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + initAdapter() + initItemDecoration() + setTripList() + observeDashBoardListState() + + } + + private fun initAdapter() { + _adapter = CompletedAdapter(this) + binding.rvDashboardCompletedTrip.adapter = adapter + } + + private fun initItemDecoration() { + val itemDeco = DashBoardDecoration(requireContext()) + binding.rvDashboardCompletedTrip.addItemDecoration(itemDeco) + } + + private fun setTripList() { + val progress = DashBoardViewModel.COMPLETED + viewModel.getTripListFromServer(progress) + } + + private fun observeDashBoardListState() { + viewModel.dashBoardCompletedListState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Success -> { + setEmptyView(false) + adapter.submitList(state.data.trips) + } + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Loading -> return@onEach + + is UiState.Empty -> setEmptyView(true) + } + }.launchIn(lifecycleScope) + } + + private fun setEmptyView(isEmpty: Boolean) { + binding.rvDashboardCompletedTrip.isVisible = !isEmpty + binding.layoutDashboardEmpty.isVisible = isEmpty + } + + + override fun onDestroyView() { + super.onDestroyView() + _adapter = null + } + + override fun onDashBoardSelectedListener(tripCreate: DashBoardTripModel) { + + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedViewHolder.kt b/presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedViewHolder.kt similarity index 67% rename from presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedViewHolder.kt rename to presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedViewHolder.kt index 8d38ed17..5ff5e9cb 100644 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/dashboard/triplist/CompletedViewHolder.kt @@ -1,7 +1,8 @@ -package com.going.presentation.tripdashboard.triplist +package com.going.presentation.dashboard.triplist import androidx.recyclerview.widget.RecyclerView -import com.going.domain.entity.response.CompletedListModel +import com.going.domain.entity.response.DashBoardModel.DashBoardTripModel +import com.going.presentation.R import com.going.presentation.databinding.ItemDashBoardCompletedBinding import com.going.ui.extension.setOnSingleClickListener @@ -10,12 +11,14 @@ class CompletedViewHolder( private val listener: CompletedAdapter.OnDashBoardSelectedListener ) : RecyclerView.ViewHolder(binding.root) { - fun onBind(item: CompletedListModel) { + fun onBind(item: DashBoardTripModel) { binding.run { tvDashboardTripTitle.text = item.title tvDashboardDateStart.text = item.startDate tvDashboardDateEnd.text = item.endDate + tvDashboardDeadlineCompleted.text = + itemView.context.getString(R.string.dashboard_tv_completed_trip) layoutDashboard.setOnSingleClickListener { listener.onDashBoardSelectedListener(item) diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/DashBoardDecoration.kt b/presentation/src/main/java/com/going/presentation/dashboard/triplist/DashBoardDecoration.kt similarity index 91% rename from presentation/src/main/java/com/going/presentation/tripdashboard/triplist/DashBoardDecoration.kt rename to presentation/src/main/java/com/going/presentation/dashboard/triplist/DashBoardDecoration.kt index 7679064a..a119978a 100644 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/DashBoardDecoration.kt +++ b/presentation/src/main/java/com/going/presentation/dashboard/triplist/DashBoardDecoration.kt @@ -1,4 +1,4 @@ -package com.going.presentation.tripdashboard.triplist +package com.going.presentation.dashboard.triplist import android.content.Context import android.graphics.Rect diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingAdapter.kt b/presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingAdapter.kt similarity index 75% rename from presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingAdapter.kt rename to presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingAdapter.kt index f08cb98d..91f58972 100644 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingAdapter.kt @@ -1,18 +1,18 @@ -package com.going.presentation.tripdashboard.triplist +package com.going.presentation.dashboard.triplist import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter -import com.going.domain.entity.response.OngoingListModel +import com.going.domain.entity.response.DashBoardModel.DashBoardTripModel import com.going.presentation.databinding.ItemDashBoardOngoingBinding import com.going.ui.extension.ItemDiffCallback class OngoingAdapter( private val listener: OnDashBoardSelectedListener -) : ListAdapter(diffUtil) { +) : ListAdapter(diffUtil) { interface OnDashBoardSelectedListener { - fun onDashBoardSelectedListener(tripCreate: OngoingListModel) + fun onDashBoardSelectedListener(tripCreate: DashBoardTripModel) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OngoingViewHolder { @@ -30,7 +30,7 @@ class OngoingAdapter( } companion object { - private val diffUtil = ItemDiffCallback( + private val diffUtil = ItemDiffCallback( onItemsTheSame = { old, new -> old.title == new.title }, onContentsTheSame = { old, new -> old == new }, ) diff --git a/presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingTripFragment.kt b/presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingTripFragment.kt new file mode 100644 index 00000000..8534a2bc --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingTripFragment.kt @@ -0,0 +1,87 @@ +package com.going.presentation.dashboard.triplist + +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.going.domain.entity.response.DashBoardModel.DashBoardTripModel +import com.going.presentation.R +import com.going.presentation.dashboard.DashBoardViewModel +import com.going.presentation.databinding.FragmentOngoingTripBinding +import com.going.ui.base.BaseFragment +import com.going.ui.extension.UiState +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class OngoingTripFragment : + BaseFragment(R.layout.fragment_ongoing_trip), + OngoingAdapter.OnDashBoardSelectedListener { + + private val viewModel by activityViewModels() + + private var _adapter: OngoingAdapter? = null + private val adapter get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + initAdapter() + initItemDecoration() + setTripList() + observeDashBoardListState() + + } + + + private fun initAdapter() { + _adapter = OngoingAdapter(this) + binding.rvDashboardOngoingTrip.adapter = adapter + } + + private fun initItemDecoration() { + val itemDeco = DashBoardDecoration(requireContext()) + binding.rvDashboardOngoingTrip.addItemDecoration(itemDeco) + } + + private fun setTripList() { + val progress = DashBoardViewModel.ONGOING + viewModel.getTripListFromServer(progress) + } + + private fun observeDashBoardListState() { + viewModel.dashBoardOngoingListState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Success -> { + setEmptyView(false) + adapter.submitList(state.data.trips) + } + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Loading -> return@onEach + + is UiState.Empty -> setEmptyView(true) + } + }.launchIn(lifecycleScope) + } + + private fun setEmptyView(isEmpty: Boolean) { + binding.rvDashboardOngoingTrip.isVisible = !isEmpty + binding.layoutDashboardEmpty.isVisible = isEmpty + } + + override fun onDestroyView() { + super.onDestroyView() + _adapter = null + } + + override fun onDashBoardSelectedListener(tripCreate: DashBoardTripModel) { + + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingViewHolder.kt b/presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingViewHolder.kt similarity index 86% rename from presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingViewHolder.kt rename to presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingViewHolder.kt index c83928dd..66a8d17e 100644 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/dashboard/triplist/OngoingViewHolder.kt @@ -1,7 +1,7 @@ -package com.going.presentation.tripdashboard.triplist +package com.going.presentation.dashboard.triplist import androidx.recyclerview.widget.RecyclerView -import com.going.domain.entity.response.OngoingListModel +import com.going.domain.entity.response.DashBoardModel.DashBoardTripModel import com.going.presentation.R import com.going.presentation.databinding.ItemDashBoardOngoingBinding import com.going.ui.extension.setOnSingleClickListener @@ -11,7 +11,7 @@ class OngoingViewHolder( private val listener: OngoingAdapter.OnDashBoardSelectedListener ) : RecyclerView.ViewHolder(binding.root) { - fun onBind(item: OngoingListModel) { + fun onBind(item: DashBoardTripModel) { binding.run { tvDashboardTripTitle.text = item.title tvDashboardDateStart.text = item.startDate diff --git a/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripActivity.kt b/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripActivity.kt index a64b189a..54940ff6 100644 --- a/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripActivity.kt +++ b/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripActivity.kt @@ -5,17 +5,25 @@ import android.os.Bundle import android.widget.TextView import androidx.activity.viewModels import androidx.core.content.res.ResourcesCompat +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.going.domain.entity.CodeState import com.going.presentation.R import com.going.presentation.databinding.ActivityEnterTripBinding +import com.going.presentation.enter.invitefinish.InviteFinishActivity import com.going.presentation.starttrip.StartTripSplashActivity import com.going.ui.base.BaseActivity +import com.going.ui.extension.UiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +@AndroidEntryPoint class EnterTripActivity : BaseActivity(R.layout.activity_enter_trip) { private val viewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -23,6 +31,7 @@ class EnterTripActivity : BaseActivity(R.layout.activi initBindingViewModel() observeIsCodeAvailable() initNextBtnClickListener() + observeEnterTripState() } @@ -76,10 +85,43 @@ class EnterTripActivity : BaseActivity(R.layout.activi counter.setTextColor(getColor(color)) } + + private fun observeEnterTripState() { + viewModel.tripState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Success -> { + + Intent(this, InviteFinishActivity::class.java).apply { + putExtra(TITLE, state.data.title) + putExtra(START, state.data.startDate) + putExtra(END, state.data.endDate) + putExtra(DAY, state.data.day) + startActivity(this) + } + } + + is UiState.Failure -> { + toast(getString(R.string.server_error)) + } + + is UiState.Loading -> return@onEach + + is UiState.Empty -> return@onEach + } + }.launchIn(lifecycleScope) + } + private fun initNextBtnClickListener() { binding.btnEnterTripNext.setOnSingleClickListener { - //다음 뷰로 이동 + viewModel.checkInviteCodeFromServer() } } + companion object { + const val TITLE = "title" + const val START = "start" + const val END = "end" + const val DAY = "day" + } + } diff --git a/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripViewModel.kt b/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripViewModel.kt index ce11be42..975aaa15 100644 --- a/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/enter/entertrip/EnterTripViewModel.kt @@ -2,10 +2,26 @@ package com.going.presentation.enter.entertrip import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.going.domain.entity.CodeState +import com.going.domain.entity.request.EnterTripRequestModel +import com.going.domain.entity.response.EnterTripModel +import com.going.domain.repository.EnterTripRepository +import com.going.ui.extension.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import java.util.regex.Pattern +import javax.inject.Inject -class EnterTripViewModel : ViewModel() { +@HiltViewModel +class EnterTripViewModel @Inject constructor( + private val enterTripRepository: EnterTripRepository +) : ViewModel() { + + private val _tripState = MutableStateFlow>(UiState.Empty) + val tripState: StateFlow> = _tripState val inviteCode = MutableLiveData() var codeLength = MutableLiveData(0) @@ -31,6 +47,18 @@ class EnterTripViewModel : ViewModel() { isCheckEnterAvailable.value = isCodeAvailable.value == CodeState.Success } + fun checkInviteCodeFromServer() { + _tripState.value = UiState.Loading + viewModelScope.launch { + enterTripRepository.postEnterTrip( + EnterTripRequestModel(inviteCode.value ?: "") + ).onSuccess { result -> + _tripState.value = UiState.Success(result) + }.onFailure { + _tripState.value = UiState.Failure(it.message.orEmpty()) + } + } + } companion object { private const val ENG_NUM_PATTERN = "^[a-z0-9]*$" diff --git a/presentation/src/main/java/com/going/presentation/enter/invitefinish/InviteFinishActivity.kt b/presentation/src/main/java/com/going/presentation/enter/invitefinish/InviteFinishActivity.kt index e4444cf1..8d902939 100644 --- a/presentation/src/main/java/com/going/presentation/enter/invitefinish/InviteFinishActivity.kt +++ b/presentation/src/main/java/com/going/presentation/enter/invitefinish/InviteFinishActivity.kt @@ -2,28 +2,52 @@ package com.going.presentation.enter.invitefinish import android.content.Intent import android.os.Bundle -import androidx.activity.viewModels import com.going.presentation.R import com.going.presentation.databinding.ActivityInviteFinishBinding import com.going.presentation.enter.entertrip.EnterTripActivity +import com.going.presentation.enter.entertrip.EnterTripActivity.Companion.DAY +import com.going.presentation.enter.entertrip.EnterTripActivity.Companion.END +import com.going.presentation.enter.entertrip.EnterTripActivity.Companion.START +import com.going.presentation.enter.entertrip.EnterTripActivity.Companion.TITLE import com.going.presentation.preferencetag.PreferenceTagActivity import com.going.ui.base.BaseActivity import com.going.ui.extension.setOnSingleClickListener +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class InviteFinishActivity : BaseActivity(R.layout.activity_invite_finish) { - private val viewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + getServerList() initBackBtnClickListener() initEnterBtnClickListener() } + private fun getServerList() { + val serverlist = getIntent() + + if (serverlist != null) { + val title = intent.getStringExtra(TITLE) + val start = intent.getStringExtra(START) + val end = intent.getStringExtra(END) + val day = intent.getIntExtra(DAY, 0) + + binding.tvInviteFinishName.text = title + binding.tvInviteFinishDay.text = String.format(DATE_FORMAT, start, end) + + if (day > 0) { + binding.tvInviteFinishDayLeft.text = String.format(D_DAY_FORMAT, day) + } else { + binding.tvInviteFinishDayLeft.text = TRIP_FORMAT + } + } + } + private fun initBackBtnClickListener() { binding.btnInviteFinishBack.setOnSingleClickListener { Intent(this, EnterTripActivity::class.java).apply { @@ -40,6 +64,12 @@ class InviteFinishActivity : } } + companion object { + const val DATE_FORMAT = "%s - %s" + const val D_DAY_FORMAT = "D - %d" + const val TRIP_FORMAT = "여행중" + } + } diff --git a/presentation/src/main/java/com/going/presentation/enter/invitefinish/InviteFinishViewModel.kt b/presentation/src/main/java/com/going/presentation/enter/invitefinish/InviteFinishViewModel.kt deleted file mode 100644 index 8b15aabb..00000000 --- a/presentation/src/main/java/com/going/presentation/enter/invitefinish/InviteFinishViewModel.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.going.presentation.enter.invitefinish - -import androidx.lifecycle.ViewModel - -class InviteFinishViewModel : ViewModel() { -} diff --git a/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt b/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt index 8bd913d7..dd44fa03 100644 --- a/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt +++ b/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt @@ -26,27 +26,27 @@ class SettingActivity : BaseActivity(R.layout.activity_s } private fun initProfileClickListener() { - binding.layoutSettingProfile.setOnSingleClickListener { + binding.btnSettingProfile.setOnSingleClickListener { } } private fun initInquireClickListener() { - binding.layoutSettingInquire.setOnSingleClickListener { + binding.btnSettingInquire.setOnSingleClickListener { } } private fun initPolicyClickListener() { - binding.layoutSettingPolicy.setOnSingleClickListener { + binding.btnSettingPolicy.setOnSingleClickListener { } } private fun initAboutDooripClickListener() { - binding.layoutSettingAboutDoorip.setOnSingleClickListener { + binding.btnSettingAboutDoorip.setOnSingleClickListener { } } private fun initLogoutClickListener() { - binding.layoutSettingLogout.setOnSingleClickListener { + binding.btnSettingLogout.setOnSingleClickListener { showLogoutAlertDialog() } } diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/PrivateDetailActivity.kt b/presentation/src/main/java/com/going/presentation/todo/detail/PrivateDetailActivity.kt new file mode 100644 index 00000000..4046aaa1 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/detail/PrivateDetailActivity.kt @@ -0,0 +1,100 @@ +package com.going.presentation.todo.detail + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.going.presentation.R +import com.going.presentation.databinding.ActivityPrivateDetailBinding +import com.going.presentation.todo.detail.PublicDetailActivity.Companion.EXTRA_TODO_ID +import com.going.ui.base.BaseActivity +import com.going.ui.extension.EnumUiState +import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class PrivateDetailActivity : + BaseActivity(R.layout.activity_private_detail) { + + private val viewModel by viewModels() + + private var todoId: Long = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + initViewModel() + initBackBtnClickListener() + initDeleteBtnClickListener() + initModBtnClickListener() + getTodoId() + setDetailData() + observeTodoDetailState() + observeTodoDeleteState() + } + + private fun initViewModel() { + binding.vm = viewModel + } + + private fun initBackBtnClickListener() { + binding.btnMyTodoDetailBack.setOnSingleClickListener { + finish() + } + } + + private fun initDeleteBtnClickListener() { + binding.btnMyTodoDetailDelete.setOnSingleClickListener { + viewModel.deleteTodoFromServer(todoId) + } + } + + private fun initModBtnClickListener() { + binding.btnMyTodoDetailMod.setOnSingleClickListener { + toast(getString(R.string.will_be_update)) + } + } + + private fun getTodoId() { + todoId = intent.getLongExtra(EXTRA_TODO_ID, 0) + } + + private fun setDetailData() { + viewModel.getTodoDetailFromServer(todoId) + } + + private fun observeTodoDetailState() { + viewModel.todoDetailState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + EnumUiState.LOADING -> return@onEach + + EnumUiState.SUCCESS -> return@onEach + + EnumUiState.FAILURE -> toast(getString(R.string.server_error)) + + EnumUiState.EMPTY -> return@onEach + } + }.launchIn(lifecycleScope) + } + + private fun observeTodoDeleteState() { + viewModel.todoDeleteState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + EnumUiState.LOADING -> return@onEach + + EnumUiState.SUCCESS -> { + toast(getString(R.string.todo_delete_toast)) + finish() + } + + EnumUiState.FAILURE -> toast(getString(R.string.server_error)) + + EnumUiState.EMPTY -> return@onEach + } + }.launchIn(lifecycleScope) + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/PrivateDetailViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/detail/PrivateDetailViewModel.kt new file mode 100644 index 00000000..6d126f87 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/detail/PrivateDetailViewModel.kt @@ -0,0 +1,69 @@ +package com.going.presentation.todo.detail + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.going.domain.repository.TodoRepository +import com.going.ui.extension.EnumUiState +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 PrivateDetailViewModel @Inject constructor( + private val todoRepository: TodoRepository +) : ViewModel() { + + val todo = MutableLiveData("") + val nowTodoLength = MutableLiveData(0) + + val endDate = MutableLiveData("") + + val memo = MutableLiveData("") + val nowMemoLength = MutableLiveData(0) + + private val _todoDetailState = MutableStateFlow(EnumUiState.EMPTY) + val todoDetailState: StateFlow = _todoDetailState + + private val _todoDeleteState = MutableStateFlow(EnumUiState.EMPTY) + val todoDeleteState: StateFlow = _todoDeleteState + + fun getTodoDetailFromServer(todoId: Long) { + _todoDetailState.value = EnumUiState.LOADING + viewModelScope.launch { + todoRepository.getTodoDetail(todoId) + .onSuccess { response -> + _todoDetailState.value = EnumUiState.SUCCESS + todo.value = response.title + endDate.value = response.endDate + memo.value = response.memo + nowTodoLength.value = response.title.length + nowMemoLength.value = response.memo.length + } + .onFailure { + _todoDetailState.value = EnumUiState.FAILURE + } + } + } + + fun deleteTodoFromServer(todoId: Long) { + _todoDeleteState.value = EnumUiState.LOADING + viewModelScope.launch { + todoRepository.deleteTodo(todoId) + .onSuccess { + _todoDeleteState.value = EnumUiState.SUCCESS + } + .onFailure { + _todoDeleteState.value = EnumUiState.FAILURE + } + } + } + + companion object { + const val MAX_TODO_LEN = 15 + const val MAX_MEMO_LEN = 1000 + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/PublicDetailActivity.kt b/presentation/src/main/java/com/going/presentation/todo/detail/PublicDetailActivity.kt new file mode 100644 index 00000000..13d9552e --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/detail/PublicDetailActivity.kt @@ -0,0 +1,118 @@ +package com.going.presentation.todo.detail + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.going.presentation.R +import com.going.presentation.databinding.ActivityPublicDetailBinding +import com.going.ui.base.BaseActivity +import com.going.ui.extension.EnumUiState +import com.going.ui.extension.UiState +import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class PublicDetailActivity : + BaseActivity(R.layout.activity_public_detail) { + + private val viewModel by viewModels() + + private var _adapter: TodoDetailNameAdapter? = null + private val adapter + get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } + + private var todoId: Long = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + initViewModel() + initNameListAdapter() + initBackBtnClickListener() + initDeleteBtnClickListener() + initModBtnClickListener() + getTodoId() + setDetailData() + observeTodoDetailState() + observeTodoDeleteState() + } + + private fun initViewModel() { + binding.vm = viewModel + } + + private fun initNameListAdapter() { + _adapter = TodoDetailNameAdapter() + binding.rvOurTodoDetailPerson.adapter = adapter + } + + private fun initBackBtnClickListener() { + binding.btnOurTodoDetailBack.setOnSingleClickListener { + finish() + } + } + + private fun initDeleteBtnClickListener() { + binding.btnOurTodoDetailDelete.setOnSingleClickListener { + viewModel.deleteTodoFromServer(todoId) + } + } + + private fun initModBtnClickListener() { + binding.btnOurTodoDetailMod.setOnSingleClickListener { + toast(getString(R.string.will_be_update)) + } + } + + private fun getTodoId() { + todoId = intent.getLongExtra(EXTRA_TODO_ID, 0) + } + + private fun setDetailData() { + viewModel.getTodoDetailFromServer(todoId) + } + + private fun observeTodoDetailState() { + viewModel.todoDetailState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Loading -> return@onEach + + is UiState.Success -> adapter.submitList(state.data) + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Empty -> return@onEach + } + }.launchIn(lifecycleScope) + } + + private fun observeTodoDeleteState() { + viewModel.todoDeleteState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + EnumUiState.LOADING -> return@onEach + + EnumUiState.SUCCESS -> { + toast(getString(R.string.todo_delete_toast)) + finish() + } + + EnumUiState.FAILURE -> toast(getString(R.string.server_error)) + + EnumUiState.EMPTY -> return@onEach + } + }.launchIn(lifecycleScope) + } + + override fun onDestroy() { + super.onDestroy() + _adapter = null + } + + companion object { + const val EXTRA_TODO_ID = "EXTRA_TODO_ID" + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/PublicDetailViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/detail/PublicDetailViewModel.kt new file mode 100644 index 00000000..b4b9e36b --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/detail/PublicDetailViewModel.kt @@ -0,0 +1,71 @@ +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.repository.TodoRepository +import com.going.ui.extension.EnumUiState +import com.going.ui.extension.UiState +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 PublicDetailViewModel @Inject constructor( + private val todoRepository: TodoRepository +) : ViewModel() { + + val todo = MutableLiveData("") + val nowTodoLength = MutableLiveData(0) + + val endDate = MutableLiveData("") + + val memo = MutableLiveData("") + val nowMemoLength = MutableLiveData(0) + + private val _todoDetailState = MutableStateFlow>>(UiState.Empty) + val todoDetailState: StateFlow>> = _todoDetailState + + private val _todoDeleteState = MutableStateFlow(EnumUiState.EMPTY) + val todoDeleteState: StateFlow = _todoDeleteState + + fun getTodoDetailFromServer(todoId: Long) { + _todoDetailState.value = UiState.Loading + viewModelScope.launch { + todoRepository.getTodoDetail(todoId) + .onSuccess { response -> + todo.value = response.title + endDate.value = response.endDate + memo.value = response.memo + nowTodoLength.value = response.title.length + nowMemoLength.value = response.memo.length + _todoDetailState.value = UiState.Success(response.allocators) + } + .onFailure { + _todoDetailState.value = UiState.Failure(it.message.toString()) + } + } + } + + fun deleteTodoFromServer(todoId: Long) { + _todoDeleteState.value = EnumUiState.LOADING + viewModelScope.launch { + todoRepository.deleteTodo(todoId) + .onSuccess { + _todoDeleteState.value = EnumUiState.SUCCESS + } + .onFailure { + _todoDeleteState.value = EnumUiState.FAILURE + } + } + } + + companion object { + const val MAX_TODO_LEN = 15 + const val MAX_MEMO_LEN = 1000 + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailNameAdapter.kt b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailNameAdapter.kt new file mode 100644 index 00000000..fc6fda5e --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailNameAdapter.kt @@ -0,0 +1,28 @@ +package com.going.presentation.todo.detail + +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.extension.ItemDiffCallback + +class TodoDetailNameAdapter : ListAdapter(diffUtil) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoDetailNameViewHolder { + val binding: ItemTodoCreateNameBinding = + ItemTodoCreateNameBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return TodoDetailNameViewHolder(binding) + } + + override fun onBindViewHolder(holder: TodoDetailNameViewHolder, position: Int) { + holder.onBind(getItem(position)) + } + + companion object { + private val diffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old.name == new.name }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailNameViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailNameViewHolder.kt new file mode 100644 index 00000000..46d9bb57 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/todo/detail/TodoDetailNameViewHolder.kt @@ -0,0 +1,26 @@ +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 + +class TodoDetailNameViewHolder( + val binding: ItemTodoCreateNameBinding +) : RecyclerView.ViewHolder(binding.root) { + + 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) + } + } + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoFragment.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoFragment.kt index be1654ec..585caeb7 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoFragment.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoFragment.kt @@ -15,7 +15,9 @@ import com.going.presentation.databinding.FragmentMyTodoBinding import com.going.presentation.todo.mytodo.create.MyTodoCreateActivity import com.going.presentation.todo.mytodo.todolist.MyTodoViewPagerAdapter import com.going.ui.base.BaseFragment +import com.going.ui.extension.UiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn @@ -32,9 +34,11 @@ class MyTodoFragment() : BaseFragment(R.layout.fragment_m super.onViewCreated(view, savedInstanceState) initAddTodoListener() + setMyTripInfo() setTabLayout() setViewPager() setTodoCountText() + observeMyTripInfoState() observeTotalUncompletedTodoCount() } @@ -46,6 +50,12 @@ class MyTodoFragment() : BaseFragment(R.layout.fragment_m } } + private fun setMyTripInfo() { + // TODO: tripId + val tripId : Long = 1 + viewModel.getMyTripInfoFromServer(tripId) + } + private fun setTabLayout() { binding.tabMyTodo.apply { for (tabName in tabTextList) { @@ -68,6 +78,20 @@ class MyTodoFragment() : BaseFragment(R.layout.fragment_m setTodoCount(viewModel.totalUncompletedTodoCount.value) } + private fun observeMyTripInfoState() { + viewModel.myTripInfoState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Loading -> return@onEach + + is UiState.Success -> binding.tvMyTodoTitleUp.text = state.data.name + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Empty -> return@onEach + } + }.launchIn(lifecycleScope) + } + private fun observeTotalUncompletedTodoCount() { viewModel.totalUncompletedTodoCount.flowWithLifecycle(lifecycle).onEach { count -> setTodoCount(count) diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoViewModel.kt index 4cd33bd7..4d63c7e1 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/MyTodoViewModel.kt @@ -2,6 +2,7 @@ package com.going.presentation.todo.mytodo import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.going.domain.entity.response.MyTripInfoModel import com.going.domain.entity.response.TodoModel import com.going.domain.repository.TodoRepository import com.going.presentation.todo.ourtodo.OurTodoViewModel @@ -17,6 +18,9 @@ class MyTodoViewModel @Inject constructor( private val todoRepository: TodoRepository ) : ViewModel() { + private val _myTripInfoState = MutableStateFlow>(UiState.Empty) + val myTripInfoState: StateFlow> = _myTripInfoState + private val _totalUncompletedTodoCount = MutableStateFlow(0) val totalUncompletedTodoCount: StateFlow = _totalUncompletedTodoCount @@ -30,6 +34,19 @@ class MyTodoViewModel @Inject constructor( _totalUncompletedTodoCount.value = _totalUncompletedTodoCount.value - 1 } + fun getMyTripInfoFromServer(tripId: Long) { + _myTripInfoState.value = UiState.Loading + viewModelScope.launch { + todoRepository.getMyTripInfo(tripId) + .onSuccess { response -> + _myTripInfoState.value = UiState.Success(response) + } + .onFailure { + _myTripInfoState.value = UiState.Failure(it.message.toString()) + } + } + } + fun getUncompleteTodoListFromServer(tripId: Long, category: String, progress: String) { _todoUncompleteListState.value = UiState.Loading viewModelScope.launch { diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt index 99cdfa59..8ef8aa20 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt @@ -5,11 +5,19 @@ import android.os.Bundle import android.widget.TextView import androidx.activity.viewModels import androidx.core.content.res.ResourcesCompat +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.ActivityMyTodoCreateBinding import com.going.ui.base.BaseActivity +import com.going.ui.extension.UiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +@AndroidEntryPoint class MyTodoCreateActivity : BaseActivity(R.layout.activity_my_todo_create) { @@ -26,6 +34,7 @@ class MyTodoCreateActivity : initDateClickListener() initFinishBtnListener() initBackBtnListener() + observeTodoCreateState() observeTextLength() observeMemoLength() observeDateEmpty() @@ -68,8 +77,10 @@ class MyTodoCreateActivity : private fun initFinishBtnListener() { binding.btnMyTodoMemoFinish.setOnSingleClickListener { - // 서버통신 진행 - finish() + // tripId, allocatorId 는 임시 설정 + val tripId: Long = 1 + val participantId: Long = 3 + viewModel.postToCreateTodoFromServer(tripId, participantId) } } @@ -79,6 +90,23 @@ class MyTodoCreateActivity : } } + private fun observeTodoCreateState() { + viewModel.todoCreateState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Success -> { + toast(getString(R.string.todo_create_toast)) + finish() + } + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Loading -> return@onEach + + is UiState.Empty -> return@onEach + } + }.launchIn(lifecycleScope) + } + private fun observeTextLength() { viewModel.nowTodoLength.observe(this) { length -> val maxTodoLen = viewModel.getMaxTodoLen() diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateBottomSheet.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateBottomSheet.kt index aa0d64ad..227edcc5 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateBottomSheet.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateBottomSheet.kt @@ -26,11 +26,17 @@ class MyTodoCreateBottomSheet() : 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() + "." + (binding.dpCreateTripDate.month + 1).toString() + "." + binding.dpCreateTripDate.dayOfMonth.toString() + 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/mytodo/create/MyTodoCreateViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateViewModel.kt index d1654b95..cd47a4dc 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateViewModel.kt @@ -2,9 +2,21 @@ package com.going.presentation.todo.mytodo.create import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.going.domain.entity.request.TodoCreateRequestModel +import com.going.domain.repository.TodoRepository +import com.going.ui.extension.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import java.text.BreakIterator +import javax.inject.Inject -class MyTodoCreateViewModel : ViewModel() { +@HiltViewModel +class MyTodoCreateViewModel @Inject constructor( + private val todoRepository: TodoRepository +) : ViewModel() { val todo = MutableLiveData("") val nowTodoLength = MutableLiveData(0) @@ -16,6 +28,9 @@ class MyTodoCreateViewModel : ViewModel() { val isFinishAvailable = MutableLiveData(false) + private val _todoCreateState = MutableStateFlow>(UiState.Empty) + val todoCreateState: StateFlow> = _todoCreateState + fun getMaxTodoLen() = MAX_TODO_LEN fun getMaxMemoLen() = MAX_MEMO_LEN @@ -24,7 +39,29 @@ class MyTodoCreateViewModel : ViewModel() { nowTodoLength.value = getGraphemeLength(todo.value) nowMemoLength.value = getGraphemeLength(memo.value) isFinishAvailable.value = - todo.value?.isNotEmpty() == true && memo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true + todo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true + } + + fun postToCreateTodoFromServer(tripId: Long, participantId: Long) { + _todoCreateState.value = UiState.Loading + viewModelScope.launch { + todoRepository.postToCreateTodo( + tripId = tripId, + request = TodoCreateRequestModel( + title = todo.value ?: "", + endDate = endDate.value ?: "", + allocators = listOf(participantId), + memo = memo.value, + secret = true + ) + ) + .onSuccess { response -> + _todoCreateState.value = UiState.Success(response) + } + .onFailure { + _todoCreateState.value = UiState.Failure(it.message.toString()) + } + } } // 이모지 포함 글자 수 세는 함수 diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/detail/MyTodoDetailActivity.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/detail/MyTodoDetailActivity.kt deleted file mode 100644 index a69e8b7a..00000000 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/detail/MyTodoDetailActivity.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.going.presentation.todo.mytodo.detail - -import android.os.Bundle -import androidx.activity.viewModels -import com.going.presentation.R -import com.going.presentation.databinding.ActivityMyTodoDetailBinding -import com.going.ui.base.BaseActivity -import com.going.ui.extension.setOnSingleClickListener -import com.going.ui.extension.toast - -class MyTodoDetailActivity : - BaseActivity(R.layout.activity_my_todo_detail) { - - private val viewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - initViewModel() - initBackBtnClickListener() - initDeleteBtnClickListener() - initModBtnClickListener() - setDetailData() - } - - private fun initViewModel() { - binding.vm = viewModel - } - - private fun initBackBtnClickListener() { - binding.btnMyTodoDetailBack.setOnSingleClickListener { - finish() - } - } - - private fun initDeleteBtnClickListener() { - binding.btnMyTodoDetailDelete.setOnSingleClickListener { - // 삭제 서버 통신 - finish() - } - } - - private fun initModBtnClickListener() { - binding.btnMyTodoDetailMod.setOnSingleClickListener { - toast(getString(R.string.will_be_update)) - } - } - - private fun setDetailData() { - intent.getLongExtra(EXTRA_TODO_ID,0) - // 추후 todoId를 보내서 받는 서버통신으로 변경 - viewModel.todo.value = "맛있는 밥 먹기" - viewModel.endDate.value = "2024.1.10" - viewModel.memo.value = "오늘 완전 완전 맛있는 파스타를 먹었는데 완전 아주 그냥 이게 말이지" - } - - companion object { - const val EXTRA_TODO_ID = "EXTRA_TODO_ID" - } - -} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/detail/MyTodoDetailViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/detail/MyTodoDetailViewModel.kt deleted file mode 100644 index 2896fcf2..00000000 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/detail/MyTodoDetailViewModel.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.going.presentation.todo.mytodo.detail - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -class MyTodoDetailViewModel : ViewModel() { - - val todo = MutableLiveData("") - val nowTodoLength = MutableLiveData(0) - - val endDate = MutableLiveData("") - - val memo = MutableLiveData("") - val nowMemoLength = MutableLiveData(0) - - companion object { - const val MAX_TODO_LEN = 15 - const val MAX_MEMO_LEN = 1000 - } - -} \ No newline at end of file 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 227edfa4..60d19a8f 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 @@ -1,5 +1,6 @@ package com.going.presentation.todo.mytodo.todolist +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.View @@ -8,10 +9,12 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.FragmentMyTodoCompleteBinding +import com.going.presentation.todo.detail.PrivateDetailActivity +import com.going.presentation.todo.detail.PublicDetailActivity +import com.going.presentation.todo.detail.PublicDetailActivity.Companion.EXTRA_TODO_ID import com.going.presentation.todo.mytodo.MyTodoViewModel import com.going.presentation.todo.mytodo.MyTodoViewModel.Companion.COMPLETE import com.going.presentation.todo.mytodo.MyTodoViewModel.Companion.MY_TODO -import com.going.presentation.todo.mytodo.detail.MyTodoDetailActivity import com.going.ui.base.BaseFragment import com.going.ui.extension.UiState import com.going.ui.extension.toast @@ -44,17 +47,25 @@ class MyTodoCompleteFragment() : adapter.removeItem(position) adapter.notifyDataSetChanged() }, - { todoId -> - Intent(activity, MyTodoDetailActivity::class.java).apply { - putExtra(MyTodoDetailActivity.EXTRA_TODO_ID, todoId) - startActivity(this) + { todoModel -> + if (todoModel.allocators.size <= 1) { + startDetailActivity(PrivateDetailActivity::class.java, todoModel.todoId) + } else { + startDetailActivity(PublicDetailActivity::class.java, todoModel.todoId) } }) binding.rvMyTodoComplete.adapter = adapter } + private fun startDetailActivity(targetActivity: Class<*>, todoId: Long) { + Intent(activity, targetActivity).apply { + putExtra(EXTRA_TODO_ID, todoId) + activity?.startActivity(this) + } + } + private fun setTodoList() { - // 추후 tripId 설정 + // TODO: 추후 tripId 설정 val tripId: Long = 1 viewModel.getCompleteTodoListFromServer(tripId, MY_TODO, COMPLETE) } @@ -62,7 +73,7 @@ class MyTodoCompleteFragment() : private fun observeTodoListState() { viewModel.todoCompleteListState.flowWithLifecycle(lifecycle).onEach { state -> when (state) { - is UiState.Success -> adapter.setItemList(state.data) + is UiState.Success -> adapter.submitList(state.data) is UiState.Failure -> toast(getString(R.string.server_error)) diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListAdapter.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListAdapter.kt index 169630c7..847cabfc 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListAdapter.kt @@ -2,18 +2,17 @@ package com.going.presentation.todo.mytodo.todolist import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.ListAdapter import com.going.domain.entity.response.TodoModel import com.going.presentation.databinding.ItemMyTodoBinding +import com.going.ui.extension.ItemDiffCallback class MyTodoListAdapter( private val isCompleted: Boolean, private val itemSelect: (Int) -> Unit, private val itemUnselect: (Int) -> Unit, - private val itemDetailClick: (Long) -> Unit -) : RecyclerView.Adapter() { - - private var itemList = mutableListOf() + private val itemDetailClick: (TodoModel) -> Unit +) : ListAdapter(diffUtil) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyTodoListViewHolder { val binding: ItemMyTodoBinding = @@ -22,19 +21,18 @@ class MyTodoListAdapter( } override fun onBindViewHolder(holder: MyTodoListViewHolder, position: Int) { - holder.onBind(itemList[position], position) + holder.onBind(getItem(position), position) } - override fun getItemCount(): Int = itemList.size - fun removeItem(position: Int) { - itemList.removeAt(position) - notifyItemRemoved(position) - notifyItemRangeChanged(position, itemCount) + // itemList.removeAt(position) + notifyDataSetChanged() } - fun setItemList(newItems: List) { - this.itemList.addAll(newItems) - notifyItemRangeInserted(0, itemCount) + companion object { + private val diffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old.todoId == new.todoId }, + onContentsTheSame = { old, new -> old == new }, + ) } } \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListViewHolder.kt index b7575f8f..348809d4 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/todolist/MyTodoListViewHolder.kt @@ -14,7 +14,7 @@ class MyTodoListViewHolder( private val isCompleted: Boolean, private val itemSelect: (Int) -> Unit, private val itemUnselect: (Int) -> Unit, - private val itemDetailClick: (Long) -> Unit + private val itemDetailClick: (TodoModel) -> Unit ) : RecyclerView.ViewHolder(binding.root) { fun onBind(item: TodoModel, position: Int) { @@ -55,7 +55,7 @@ class MyTodoListViewHolder( } root.setOnSingleClickListener { - itemDetailClick(item.todoId) + itemDetailClick(item) } } } 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 f2fe9155..1b21740c 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 @@ -1,5 +1,6 @@ package com.going.presentation.todo.mytodo.todolist +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.View @@ -11,8 +12,9 @@ import com.going.presentation.databinding.FragmentMyTodoUncompleteBinding import com.going.presentation.todo.mytodo.MyTodoViewModel import com.going.presentation.todo.mytodo.MyTodoViewModel.Companion.MY_TODO import com.going.presentation.todo.mytodo.MyTodoViewModel.Companion.UNCOMPLETE -import com.going.presentation.todo.mytodo.detail.MyTodoDetailActivity -import com.going.presentation.todo.mytodo.detail.MyTodoDetailActivity.Companion.EXTRA_TODO_ID +import com.going.presentation.todo.detail.PrivateDetailActivity +import com.going.presentation.todo.detail.PublicDetailActivity +import com.going.presentation.todo.detail.PublicDetailActivity.Companion.EXTRA_TODO_ID import com.going.ui.base.BaseFragment import com.going.ui.extension.UiState import com.going.ui.extension.toast @@ -34,10 +36,15 @@ class MyTodoUncompleteFragment() : super.onViewCreated(view, savedInstanceState) initAdapterWithClickListener() - setTodoList() observeTodoListState() } + override fun onResume() { + super.onResume() + + setTodoList() + } + private fun initAdapterWithClickListener() { _adapter = MyTodoListAdapter(false, { position -> @@ -46,15 +53,23 @@ class MyTodoUncompleteFragment() : viewModel.decreaseTodoCount() }, { }, - { todoId -> - Intent(activity, MyTodoDetailActivity::class.java).apply { - putExtra(EXTRA_TODO_ID, todoId) - startActivity(this) + { todoModel -> + if (todoModel.allocators.size <= 1) { + startDetailActivity(activity, PrivateDetailActivity::class.java, todoModel.todoId) + } else { + startDetailActivity(activity, PublicDetailActivity::class.java, todoModel.todoId) } }) binding.rvMyTodoUncomplete.adapter = adapter } + private fun startDetailActivity(activity: Activity?, targetActivity: Class<*>, todoId: Long) { + Intent(activity, targetActivity).apply { + putExtra(EXTRA_TODO_ID, todoId) + activity?.startActivity(this) + } + } + private fun setTodoList() { // 추후 tripId 설정 val tripId: Long = 1 @@ -64,7 +79,7 @@ class MyTodoUncompleteFragment() : private fun observeTodoListState() { viewModel.todoUncompleteListState.flowWithLifecycle(lifecycle).onEach { state -> when (state) { - is UiState.Success -> adapter.setItemList(state.data) + is UiState.Success -> adapter.submitList(state.data) is UiState.Failure -> toast(getString(R.string.server_error)) 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 01d10051..7b9a4256 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 @@ -8,14 +8,20 @@ import android.text.style.ForegroundColorSpan import android.view.View import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.FragmentOurTodoBinding import com.going.presentation.todo.ourtodo.create.OurTodoCreateActivity import com.going.presentation.todo.ourtodo.todolist.OurTodoViewPagerAdapter import com.going.ui.base.BaseFragment +import com.going.ui.extension.UiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach @AndroidEntryPoint class OurTodoFragment() : BaseFragment(R.layout.fragment_our_todo) { @@ -34,16 +40,15 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment initAdapter() initAddTodoBtnListener() initItemDecoration() - setDateTextColor() - setProgressBarStatus() + setMyTripInfo() setTabLayout() setViewPager() + observeOurTripInfoState() } private fun initAdapter() { _adapter = OurTodoFriendAdapter() binding.rvOurTripFriend.adapter = adapter - adapter.submitList(viewModel.mockParticipantsList) } private fun initAddTodoBtnListener() { @@ -59,23 +64,10 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment binding.rvOurTripFriend.addItemDecoration(itemDeco) } - - private fun setDateTextColor() { - binding.tvOurTodoTitleDown.apply { - text = SpannableStringBuilder(text).apply { - setSpan( - ForegroundColorSpan( - ContextCompat.getColor( - requireContext(), R.color.red_500 - ) - ), 6, length - 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE - ) - } - } - } - - private fun setProgressBarStatus() { - binding.progressBarOurTodo.progress = 62 + private fun setMyTripInfo() { + // TODO: tripId + val tripId: Long = 1 + viewModel.getOurTripInfoFromServer(tripId) } private fun setTabLayout() { @@ -96,6 +88,70 @@ class OurTodoFragment() : BaseFragment(R.layout.fragment }.attach() } + private fun observeOurTripInfoState() { + viewModel.ourTripInfoState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Loading -> return@onEach + + is UiState.Success -> { + binding.run { + setTitleTextWithDay(state.data.day) + tvOurTodoTitleUp.text = state.data.title + tvOurTodoTitleDate.text = getString(R.string.our_todo_date_form).format( + convertDate(state.data.startDate), + convertDate(state.data.endDate) + ) + progressBarOurTodo.progress = state.data.progress + tvOurTripInfoPercent.text = state.data.progress.toString() + "%" + adapter.submitList(state.data.participants) + } + } + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Empty -> return@onEach + } + }.launchIn(lifecycleScope) + } + + private fun convertDate(date: String): String { + val splitDate = date.split(".") + return getString(R.string.our_todo_day_form).format(splitDate[1], splitDate[2]) + } + + private fun setTitleTextWithDay(day: Int) { + when { + day > 0 -> { + binding.tvOurTodoTitleDown.text = getString(R.string.our_todo_title_down_before).format(day) + setDateTextColor(6, 6) + } + + day == 0 -> { + binding.tvOurTodoTitleDown.text = getString(R.string.our_todo_title_down_during) + setDateTextColor(0, 4) + } + + else -> { + binding.tvOurTodoTitleDown.text = getString(R.string.our_todo_title_down_end) + setDateTextColor(4, 5) + } + } + } + + private fun setDateTextColor(start: Int, end: Int) { + binding.tvOurTodoTitleDown.apply { + text = SpannableStringBuilder(text).apply { + setSpan( + ForegroundColorSpan( + ContextCompat.getColor( + requireContext(), R.color.red_500 + ) + ), start, length - end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + } + } + override fun onDestroyView() { super.onDestroyView() _adapter = null diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendAdapter.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendAdapter.kt index 089f4245..67a7aee3 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendAdapter.kt @@ -3,7 +3,7 @@ package com.going.presentation.todo.ourtodo import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter -import com.going.domain.entity.response.TripParticipantsListModel.TripParticipantModel +import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.databinding.ItemTodoFriendsBinding import com.going.ui.extension.ItemDiffCallback diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendViewHolder.kt index 7de1b269..94c71ed9 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoFriendViewHolder.kt @@ -3,7 +3,7 @@ package com.going.presentation.todo.ourtodo import androidx.recyclerview.widget.RecyclerView import coil.load import coil.transform.CircleCropTransformation -import com.going.domain.entity.response.TripParticipantsListModel.TripParticipantModel +import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.R import com.going.presentation.databinding.ItemTodoFriendsBinding diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoViewModel.kt index 67404ac6..40a32417 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/OurTodoViewModel.kt @@ -2,8 +2,9 @@ package com.going.presentation.todo.ourtodo import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.going.domain.entity.response.OurTripInfoModel import com.going.domain.entity.response.TodoModel -import com.going.domain.entity.response.TripParticipantsListModel.TripParticipantModel +import com.going.domain.entity.response.TripParticipantModel import com.going.domain.repository.TodoRepository import com.going.ui.extension.UiState import dagger.hilt.android.lifecycle.HiltViewModel @@ -17,12 +18,28 @@ class OurTodoViewModel @Inject constructor( private val todoRepository: TodoRepository ) : ViewModel() { + private val _ourTripInfoState = MutableStateFlow>(UiState.Empty) + val ourTripInfoState: StateFlow> = _ourTripInfoState + private val _todoUncompleteListState = MutableStateFlow>>(UiState.Empty) val todoUncompleteListState: StateFlow>> = _todoUncompleteListState private val _todoCompleteListState = MutableStateFlow>>(UiState.Empty) val todoCompleteListState: StateFlow>> = _todoCompleteListState + fun getOurTripInfoFromServer(tripId: Long) { + _ourTripInfoState.value = UiState.Loading + viewModelScope.launch { + todoRepository.getOurTripInfo(tripId) + .onSuccess { response -> + _ourTripInfoState.value = UiState.Success(response) + } + .onFailure { + _ourTripInfoState.value = UiState.Failure(it.message.toString()) + } + } + } + fun getTodoListFromServer( tripId: Long, category: String, progress: String ) { @@ -48,13 +65,4 @@ class OurTodoViewModel @Inject constructor( const val UNCOMPLETE = "incomplete" const val COMPLETE = "complete" } - - val mockParticipantsList: List = listOf( - TripParticipantModel(0, "일지민", 100), - TripParticipantModel(1, "이지민", 100), - TripParticipantModel(2, "삼지민", 100), - TripParticipantModel(3, "사지민", 100), - TripParticipantModel(4, "오지민", 100), - TripParticipantModel(5, "육지민", 100) - ) } \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt index 7ee77fce..92838875 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt @@ -5,11 +5,19 @@ import android.os.Bundle import android.widget.TextView import androidx.activity.viewModels import androidx.core.content.res.ResourcesCompat +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.ActivityOurTodoCreateBinding import com.going.ui.base.BaseActivity +import com.going.ui.extension.UiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +@AndroidEntryPoint class OurTodoCreateActivity : BaseActivity(R.layout.activity_our_todo_create) { @@ -31,6 +39,7 @@ class OurTodoCreateActivity : initDateClickListener() initFinishBtnListener() initBackBtnListener() + observeTodoCreateState() observeTextLength() observeMemoLength() observeDateEmpty() @@ -41,10 +50,10 @@ class OurTodoCreateActivity : } private fun initNameListAdapter() { - // 아워투두 뷰에서 intent로 친구목록 받아와서 적용할 예정 + // TODO: 아워투두 뷰에서 intent로 친구목록 받아와서 적용할 예정 _adapter = TodoCreateNameAdapter(false) binding.rvOurTodoCreatePerson.adapter = adapter - adapter.submitList(listOf("김상호", "박동민", "조세연", "이유빈")) + adapter.submitList(viewModel.totalParticipantList) } private fun initTodoFocusListener() { @@ -80,8 +89,10 @@ class OurTodoCreateActivity : private fun initFinishBtnListener() { binding.btnOurTodoMemoFinish.setOnSingleClickListener { - // 서버통신 진행 - finish() + // tripId는 임시 설정 + val tripId: Long = 1 + viewModel.participantList = adapter.currentList + viewModel.postToCreateTodoFromServer(tripId) } } @@ -91,6 +102,23 @@ class OurTodoCreateActivity : } } + private fun observeTodoCreateState() { + viewModel.todoCreateState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Success -> { + toast(getString(R.string.todo_create_toast)) + finish() + } + + is UiState.Failure -> toast(getString(R.string.server_error)) + + is UiState.Loading -> return@onEach + + is UiState.Empty -> return@onEach + } + }.launchIn(lifecycleScope) + } + private fun observeTextLength() { viewModel.nowTodoLength.observe(this) { length -> val maxTodoLen = viewModel.getMaxTodoLen() diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateBottomSheet.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateBottomSheet.kt index 2aeb4987..600c5221 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateBottomSheet.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateBottomSheet.kt @@ -5,6 +5,7 @@ import android.view.View import androidx.fragment.app.activityViewModels import com.going.presentation.R import com.going.presentation.databinding.FragmentOurTodoCreateBottomSheetBinding +import com.going.presentation.todo.mytodo.create.MyTodoCreateBottomSheet import com.going.ui.base.BaseBottomSheet import com.going.ui.extension.setOnSingleClickListener @@ -26,8 +27,10 @@ class OurTodoCreateBottomSheet() : private fun initFinishBtnClickListener() { binding.btnCreateTripFinish.setOnSingleClickListener { + val createdMonth = String.format(MyTodoCreateBottomSheet.TWO_DIGIT_FORMAT, binding.dpCreateTripDate.month + 1) + val createdDay = String.format(MyTodoCreateBottomSheet.TWO_DIGIT_FORMAT, binding.dpCreateTripDate.dayOfMonth) viewModel.endDate.value = - binding.dpCreateTripDate.year.toString() + "." + (binding.dpCreateTripDate.month + 1).toString() + "." + binding.dpCreateTripDate.dayOfMonth.toString() + binding.dpCreateTripDate.year.toString() + "." + createdMonth + "." + createdDay viewModel.checkIsFinishAvailable() dismiss() } diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt index 2227b143..dd93eabb 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt @@ -2,9 +2,22 @@ package com.going.presentation.todo.ourtodo.create import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.going.domain.entity.request.TodoCreateRequestModel +import com.going.domain.entity.response.TripParticipantModel +import com.going.domain.repository.TodoRepository +import com.going.ui.extension.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import java.text.BreakIterator +import javax.inject.Inject -class OurTodoCreateViewModel : ViewModel() { +@HiltViewModel +class OurTodoCreateViewModel @Inject constructor( + private val todoRepository: TodoRepository +) : ViewModel() { val todo = MutableLiveData("") val nowTodoLength = MutableLiveData(0) @@ -16,6 +29,15 @@ class OurTodoCreateViewModel : ViewModel() { val isFinishAvailable = MutableLiveData(false) + private val _todoCreateState = MutableStateFlow>(UiState.Empty) + val todoCreateState: StateFlow> = _todoCreateState + + // TODO: 추후 수정 + var totalParticipantList: List = listOf( + TripParticipantModel(3, "삼삼삼", 1), TripParticipantModel(21, "이십일", 2) + ) + var participantList: List = listOf() + fun getMaxTodoLen() = MAX_TODO_LEN fun getMaxMemoLen() = MAX_MEMO_LEN @@ -24,7 +46,29 @@ class OurTodoCreateViewModel : ViewModel() { nowTodoLength.value = getGraphemeLength(todo.value) nowMemoLength.value = getGraphemeLength(memo.value) isFinishAvailable.value = - todo.value?.isNotEmpty() == true && memo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true + todo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true + } + + fun postToCreateTodoFromServer(tripId: Long) { + _todoCreateState.value = UiState.Loading + viewModelScope.launch { + todoRepository.postToCreateTodo( + tripId = tripId, + request = TodoCreateRequestModel( + title = todo.value ?: "", + endDate = endDate.value ?: "", + allocators = participantList.map { it.participantId }, + memo = memo.value, + secret = false + ) + ) + .onSuccess { response -> + _todoCreateState.value = UiState.Success(response) + } + .onFailure { + _todoCreateState.value = UiState.Failure(it.message.toString()) + } + } } // 이모지 포함 글자 수 세는 함수 diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameAdapter.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameAdapter.kt index a9ce4ca1..2bc416ba 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameAdapter.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameAdapter.kt @@ -3,12 +3,13 @@ package com.going.presentation.todo.ourtodo.create import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter +import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.databinding.ItemTodoCreateNameBinding import com.going.ui.extension.ItemDiffCallback class TodoCreateNameAdapter( private val isFixed: Boolean -) : ListAdapter(diffUtil) { +) : ListAdapter(diffUtil) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoCreateNameViewHolder { val binding: ItemTodoCreateNameBinding = @@ -17,12 +18,12 @@ class TodoCreateNameAdapter( } override fun onBindViewHolder(holder: TodoCreateNameViewHolder, position: Int) { - holder.onBind(getItem(position)) + holder.onBind(getItem(position), position) } companion object { - private val diffUtil = ItemDiffCallback( - onItemsTheSame = { old, new -> old.length == new.length }, + private val diffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old.participantId == new.participantId }, onContentsTheSame = { old, new -> old == new }, ) } diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameViewHolder.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameViewHolder.kt index 0cabf5ba..63caf699 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameViewHolder.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/TodoCreateNameViewHolder.kt @@ -2,9 +2,9 @@ package com.going.presentation.todo.ourtodo.create import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView +import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.R import com.going.presentation.databinding.ItemTodoCreateNameBinding -import com.going.ui.extension.setOnSingleClickListener class TodoCreateNameViewHolder( val binding: ItemTodoCreateNameBinding, @@ -14,11 +14,11 @@ class TodoCreateNameViewHolder( private val whiteColor = ContextCompat.getColor(binding.root.context, R.color.white_000) private val grayColor = ContextCompat.getColor(binding.root.context, R.color.gray_300) - fun onBind(item: String) { + fun onBind(item: TripParticipantModel, position: Int) { binding.run { - tvTodoName.text = item + tvTodoName.text = item.name - if (item == "김상호") { + if (position == 0) { tvTodoName.setBackgroundResource(R.drawable.sel_todo_shape_red500_fill) } else { tvTodoName.setBackgroundResource(R.drawable.sel_todo_shape_gray400_fill) diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/detail/OurTodoDetailActivity.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/detail/OurTodoDetailActivity.kt deleted file mode 100644 index 29f7586d..00000000 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/detail/OurTodoDetailActivity.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.going.presentation.todo.ourtodo.detail - -import android.os.Bundle -import androidx.activity.viewModels -import com.going.presentation.R -import com.going.presentation.databinding.ActivityOurTodoDetailBinding -import com.going.presentation.todo.ourtodo.create.TodoCreateNameAdapter -import com.going.ui.base.BaseActivity -import com.going.ui.extension.setOnSingleClickListener -import com.going.ui.extension.toast - -class OurTodoDetailActivity : - BaseActivity(R.layout.activity_our_todo_detail) { - - private val viewModel by viewModels() - - private var _adapter: TodoCreateNameAdapter? = null - private val adapter - get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - initViewModel() - initNameListAdapter() - initBackBtnClickListener() - initDeleteBtnClickListener() - initModBtnClickListener() - setDetailData() - } - - private fun initViewModel() { - binding.vm = viewModel - } - - private fun initNameListAdapter() { - // 아워투두 뷰에서 intent로 친구목록 받아와서 적용할 예정 - _adapter = TodoCreateNameAdapter(true) - binding.rvOurTodoDetailPerson.adapter = adapter - } - - private fun initBackBtnClickListener() { - binding.btnOurTodoDetailBack.setOnSingleClickListener { - finish() - } - } - - private fun initDeleteBtnClickListener() { - binding.btnOurTodoDetailDelete.setOnSingleClickListener { - // 삭제 서버 통신 - finish() - } - } - - private fun initModBtnClickListener() { - binding.btnOurTodoDetailMod.setOnSingleClickListener { - toast(getString(R.string.will_be_update)) - } - } - - private fun setDetailData() { - intent.getLongExtra(EXTRA_TODO_ID,0) - // 추후 todoId를 보내서 받는 서버통신으로 변경 - viewModel.todo.value = "맛있는 밥 먹기" - viewModel.endDate.value = "2024.1.10" - adapter.submitList(listOf("김상호", "박동민", "조세연", "이유빈")) - viewModel.memo.value = "오늘 완전 완전 맛있는 파스타를 먹었는데 완전 아주 그냥 이게 말이지" - } - - override fun onDestroy() { - super.onDestroy() - _adapter = null - } - - companion object { - const val EXTRA_TODO_ID = "EXTRA_TODO_ID" - } -} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/detail/OurTodoDetailViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/detail/OurTodoDetailViewModel.kt deleted file mode 100644 index 67b04ab6..00000000 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/detail/OurTodoDetailViewModel.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.going.presentation.todo.ourtodo.detail - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -class OurTodoDetailViewModel : ViewModel() { - - val todo = MutableLiveData("") - val endDate = MutableLiveData("") - val memo = MutableLiveData("") - - companion object { - const val MAX_TODO_LEN = 15 - const val MAX_MEMO_LEN = 1000 - } - -} \ No newline at end of file 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 9af29180..d5dc4bc4 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 @@ -11,8 +11,8 @@ import com.going.presentation.databinding.FragmentOurTodoCompleteBinding import com.going.presentation.todo.ourtodo.OurTodoViewModel import com.going.presentation.todo.ourtodo.OurTodoViewModel.Companion.COMPLETE import com.going.presentation.todo.ourtodo.OurTodoViewModel.Companion.OUR_TODO -import com.going.presentation.todo.ourtodo.detail.OurTodoDetailActivity -import com.going.presentation.todo.ourtodo.detail.OurTodoDetailActivity.Companion.EXTRA_TODO_ID +import com.going.presentation.todo.detail.PublicDetailActivity +import com.going.presentation.todo.detail.PublicDetailActivity.Companion.EXTRA_TODO_ID import com.going.ui.base.BaseFragment import com.going.ui.extension.UiState import com.going.ui.extension.toast @@ -42,7 +42,7 @@ class OurTodoCompleteFragment() : _adapter = OurTodoListAdapter( true ) { todoId -> - Intent(activity, OurTodoDetailActivity::class.java).apply { + Intent(activity, PublicDetailActivity::class.java).apply { putExtra(EXTRA_TODO_ID, todoId) startActivity(this) } 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 533771b5..56fb269c 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 @@ -11,8 +11,8 @@ import com.going.presentation.databinding.FragmentOurTodoUncompleteBinding import com.going.presentation.todo.ourtodo.OurTodoViewModel import com.going.presentation.todo.ourtodo.OurTodoViewModel.Companion.OUR_TODO import com.going.presentation.todo.ourtodo.OurTodoViewModel.Companion.UNCOMPLETE -import com.going.presentation.todo.ourtodo.detail.OurTodoDetailActivity -import com.going.presentation.todo.ourtodo.detail.OurTodoDetailActivity.Companion.EXTRA_TODO_ID +import com.going.presentation.todo.detail.PublicDetailActivity +import com.going.presentation.todo.detail.PublicDetailActivity.Companion.EXTRA_TODO_ID import com.going.ui.base.BaseFragment import com.going.ui.extension.UiState import com.going.ui.extension.toast @@ -38,11 +38,17 @@ class OurTodoUncompleteFragment() : observeTodoListState() } + override fun onResume() { + super.onResume() + + setTodoList() + } + private fun initAdapterWithClickListener() { _adapter = OurTodoListAdapter( false ) { todoId -> - Intent(activity, OurTodoDetailActivity::class.java).apply { + Intent(activity, PublicDetailActivity::class.java).apply { putExtra(EXTRA_TODO_ID, todoId) startActivity(this) } diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/TripDashBoardViewModel.kt b/presentation/src/main/java/com/going/presentation/tripdashboard/TripDashBoardViewModel.kt deleted file mode 100644 index 996c1103..00000000 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/TripDashBoardViewModel.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.going.presentation.tripdashboard - -import androidx.lifecycle.ViewModel -import com.going.domain.entity.response.CompletedListModel -import com.going.domain.entity.response.OngoingListModel - -class TripDashBoardViewModel : ViewModel() { - - val mockOngoingList: List = listOf( - OngoingListModel( - title = "굉굉이랑 합숙", - startDate = "2024.01.01", - endDate = "2024.01.21", - day = 16 - ), - OngoingListModel( - title = "여행 제목 자리", - startDate = "2002.09.22", - endDate = "2005.03.31", - day = -10 - ), - OngoingListModel( - title = "상호랑 제주도 여행", - startDate = "2025.03.24", - endDate = "2025.03.31", - day = 100 - ), - OngoingListModel( - title = "동민이랑 서울 구경", - startDate = "2026.03.24", - endDate = "2026.03.31", - day = 200 - ), - OngoingListModel( - title = "유빈이랑 부산 여행", - startDate = "2027.03.24", - endDate = "2027.03.31", - day = 300 - ), - OngoingListModel( - title = "세연이랑 바다 구경", - startDate = "2028.03.24", - endDate = "2028.03.31", - day = 400 - ), - OngoingListModel( - title = "솝트랑 MT", - startDate = "2021.03.24", - endDate = "2021.03.31", - day = 0 - ), - OngoingListModel( - title = "굉굉 신년회", - startDate = "2024.01.02", - endDate = "2024.01.32", - day = 0 - ), - OngoingListModel( - title = "안드 단체 여행", - startDate = "2020.03.24", - endDate = "2021.03.31", - day = 100 - ), - OngoingListModel( - title = "두릅", - startDate = "2003.03.24", - endDate = "2004.03.31", - day = 1000 - ) - ) - - val mockCompletedList: List = listOf( - CompletedListModel( - title = "유빈이 생일 파티", - startDate = "2022.09.22", - endDate = "2022.09.23" - ), - CompletedListModel( - title = "굉굉이랑 만화 카페", - startDate = "2024.01.13", - endDate = "2024.01.23", - ), - CompletedListModel( - title = "굉굉이랑 파스타 먹기", - startDate = "2024.01.09", - endDate = "2024.01.09", - ), - CompletedListModel( - title = "굉굉이랑 고기 구워 먹기", - startDate = "2020.04.10", - endDate = "2020.04.22", - ) - ) -} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/profile/ProfileActivity.kt b/presentation/src/main/java/com/going/presentation/tripdashboard/profile/ProfileActivity.kt similarity index 97% rename from presentation/src/main/java/com/going/presentation/profile/ProfileActivity.kt rename to presentation/src/main/java/com/going/presentation/tripdashboard/profile/ProfileActivity.kt index 86712199..b3a67bfd 100644 --- a/presentation/src/main/java/com/going/presentation/profile/ProfileActivity.kt +++ b/presentation/src/main/java/com/going/presentation/tripdashboard/profile/ProfileActivity.kt @@ -1,4 +1,4 @@ -package com.going.presentation.profile +package com.going.presentation.tripdashboard.profile import android.os.Bundle import android.text.SpannableString @@ -63,4 +63,4 @@ class ProfileActivity : return string } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/going/presentation/profile/ProfileViewModel.kt b/presentation/src/main/java/com/going/presentation/tripdashboard/profile/ProfileViewModel.kt similarity index 96% rename from presentation/src/main/java/com/going/presentation/profile/ProfileViewModel.kt rename to presentation/src/main/java/com/going/presentation/tripdashboard/profile/ProfileViewModel.kt index 7650a4f9..e5f97df1 100644 --- a/presentation/src/main/java/com/going/presentation/profile/ProfileViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/tripdashboard/profile/ProfileViewModel.kt @@ -1,4 +1,4 @@ -package com.going.presentation.profile +package com.going.presentation.tripdashboard.profile import androidx.lifecycle.ViewModel import com.going.domain.entity.ProfileMock diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedTripFragment.kt b/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedTripFragment.kt deleted file mode 100644 index aa068dc0..00000000 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/CompletedTripFragment.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.going.presentation.tripdashboard.triplist - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.activityViewModels -import com.going.domain.entity.response.CompletedListModel -import com.going.presentation.R -import com.going.presentation.databinding.FragmentCompletedTripBinding -import com.going.presentation.tripdashboard.TripDashBoardViewModel -import com.going.ui.base.BaseFragment - -class CompletedTripFragment : - BaseFragment(R.layout.fragment_completed_trip), - CompletedAdapter.OnDashBoardSelectedListener { - - private val viewModel by activityViewModels() - - private var _adapter: CompletedAdapter? = null - private val adapter get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setRecyclerView() - initItemDecoration() - - } - - override fun onDashBoardSelectedListener(tripCreate: CompletedListModel) { - // 여행 생성 레이아웃 클릭 시 처리 - } - - private fun setRecyclerView() { - _adapter = CompletedAdapter(this) - binding.rvDashboardCompletedTrip.adapter = adapter - adapter.submitList(viewModel.mockCompletedList) - } - - private fun initItemDecoration() { - val itemDeco = DashBoardDecoration(requireContext()) - binding.rvDashboardCompletedTrip.addItemDecoration(itemDeco) - } - - override fun onDestroyView() { - super.onDestroyView() - _adapter = null - } - -} \ No newline at end of file diff --git a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingTripFragment.kt b/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingTripFragment.kt deleted file mode 100644 index 053bc6d5..00000000 --- a/presentation/src/main/java/com/going/presentation/tripdashboard/triplist/OngoingTripFragment.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.going.presentation.tripdashboard.triplist - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.activityViewModels -import com.going.domain.entity.response.OngoingListModel -import com.going.presentation.R -import com.going.presentation.databinding.FragmentOngoingTripBinding -import com.going.presentation.tripdashboard.TripDashBoardViewModel -import com.going.ui.base.BaseFragment - -class OngoingTripFragment : - BaseFragment(R.layout.fragment_ongoing_trip), - OngoingAdapter.OnDashBoardSelectedListener { - - private val viewModel by activityViewModels() - - private var _adapter: OngoingAdapter? = null - private val adapter get() = requireNotNull(_adapter) { getString(R.string.adapter_not_initialized_error_msg) } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setRecyclerView() - initItemDecoration() - - } - - override fun onDashBoardSelectedListener(tripCreate: OngoingListModel) { - // 여행 생성 레이아웃 클릭 시 처리 - } - - private fun setRecyclerView() { - _adapter = OngoingAdapter(this) - binding.rvDashboardOngoingTrip.adapter = adapter - adapter.submitList(viewModel.mockOngoingList) - } - - private fun initItemDecoration() { - val itemDeco = DashBoardDecoration(requireContext()) - binding.rvDashboardOngoingTrip.addItemDecoration(itemDeco) - } - - override fun onDestroyView() { - super.onDestroyView() - _adapter = null - } - -} \ No newline at end of file diff --git a/presentation/src/main/res/drawable/ic_empty_dashboard_doorip.xml b/presentation/src/main/res/drawable/ic_empty_dashboard_doorip.xml new file mode 100644 index 00000000..efe2f074 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_empty_dashboard_doorip.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/shape_rect_2_gray400_fill.xml b/presentation/src/main/res/drawable/shape_rect_2_gray400_fill.xml new file mode 100644 index 00000000..753b6a7d --- /dev/null +++ b/presentation/src/main/res/drawable/shape_rect_2_gray400_fill.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_rect_2_red500_fill.xml b/presentation/src/main/res/drawable/shape_rect_2_red500_fill.xml new file mode 100644 index 00000000..3db51f86 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_rect_2_red500_fill.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_invite_finish.xml b/presentation/src/main/res/layout/activity_invite_finish.xml index aa492dbe..d64b5849 100644 --- a/presentation/src/main/res/layout/activity_invite_finish.xml +++ b/presentation/src/main/res/layout/activity_invite_finish.xml @@ -5,10 +5,6 @@ - - @@ -80,7 +76,7 @@ android:background="@drawable/shape_rect_2_red100_fill" android:paddingHorizontal="8dp" android:paddingVertical="2dp" - android:text="D - 16" + tools:text="D - 16" android:textAppearance="@style/TextAppearance.Doorip.Detail2.Bold" android:textColor="@color/red_500" app:layout_constraintEnd_toEndOf="parent" @@ -93,7 +89,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:text="굉굉이랑 스페인" + tools:text="굉굉이랑 스페인" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_invite_finish_day_left" /> @@ -105,7 +101,7 @@ android:layout_height="wrap_content" android:layout_marginTop="2dp" android:maxLines="2" - android:text="2023.03.24 - 2023.03.31" + tools:text="2023.03.24 - 2023.03.31" android:textColor="@color/gray_300" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/presentation/src/main/res/layout/activity_my_todo_detail.xml b/presentation/src/main/res/layout/activity_private_detail.xml similarity index 99% rename from presentation/src/main/res/layout/activity_my_todo_detail.xml rename to presentation/src/main/res/layout/activity_private_detail.xml index a4e8a739..e0cd3312 100644 --- a/presentation/src/main/res/layout/activity_my_todo_detail.xml +++ b/presentation/src/main/res/layout/activity_private_detail.xml @@ -9,7 +9,7 @@ + type="com.going.presentation.todo.detail.PrivateDetailViewModel" /> + type="com.going.presentation.todo.detail.PublicDetailViewModel" /> + app:layout_constraintTop_toBottomOf="@id/btn_setting_profile"> + app:layout_constraintTop_toBottomOf="@id/btn_setting_inquire"> + app:layout_constraintTop_toBottomOf="@id/btn_setting_service_version"> + app:layout_constraintTop_toBottomOf="@id/btn_setting_policy"> + app:layout_constraintTop_toBottomOf="@id/btn_setting_about_doorip"> + tools:context=".dashboard.DashBoardActivity"> + + + + + + + + + diff --git a/presentation/src/main/res/layout/fragment_ongoing_trip.xml b/presentation/src/main/res/layout/fragment_ongoing_trip.xml index e94898a9..55966c5f 100644 --- a/presentation/src/main/res/layout/fragment_ongoing_trip.xml +++ b/presentation/src/main/res/layout/fragment_ongoing_trip.xml @@ -18,12 +18,48 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" + android:visibility="visible" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/item_dash_board_ongoing" /> + + + + + + + + diff --git a/presentation/src/main/res/layout/fragment_our_todo.xml b/presentation/src/main/res/layout/fragment_our_todo.xml index 054124f4..f9130362 100644 --- a/presentation/src/main/res/layout/fragment_our_todo.xml +++ b/presentation/src/main/res/layout/fragment_our_todo.xml @@ -70,20 +70,20 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="24dp" - android:text="우쥬랑 스페인" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + tools:text="우쥬랑 스페인" /> + app:layout_constraintTop_toBottomOf="@id/tv_our_todo_title_up" + tools:text="여행일까지 16일 남았어요!" /> + app:layout_constraintTop_toBottomOf="@id/tv_our_todo_title_down" + tools:text="12월 16일 - 12월 25일" /> + app:layout_constraintTop_toTopOf="@id/tv_our_trip_info_title" + tools:text="62%" /> 여행 친구들 초대 같이 할일 + 여행일까지 %s일 남았어요! + 여행 중이에요! + 여행이 완료되었어요! + %s - %s + %s월 %s일 나의 할일 @@ -83,11 +88,13 @@ 메모 메모를 입력해주세요. 저장 + 할일을 추가했어요 - + 할일 조회 삭제하기 수정하기 + 할일을 삭제했어요 이번 여행은! @@ -96,7 +103,7 @@ %s님의 여행 - 생성된 여행이 없어요. + 새로운 여행을 시작해 보세요 여행 생성하기 여행종료 - @@ -154,6 +161,7 @@ 초대받은 여행이 맞는지\n 확인해 주세요 초대받은 여행이 맞아요! + 여행중 여행 친구들