Skip to content

Commit

Permalink
feat: 댓글방 댓글 조회 api 연결 (#116)
Browse files Browse the repository at this point in the history
* feat: dto 및 mapper 구현

* feat: 댓글방 목록 service 구현

* feat: 댓글방 목록 data source 구현

* feat: 댓글방 목록 repository 및 model 구현

* feat: 댓글방 목록 view type을 활용한 recyclerview 구현 및 데이터 바인딩

* feat: polling 기능 구현

* feat: 댓글 스크롤 구현 (새로운 댓글이 생길시 스크롤 아래로)

* feat: 총대와 다른 참가자 이미지 리소스 파일
  • Loading branch information
chaehyuns authored and ChooSeoyeon committed Oct 11, 2024
1 parent 8843bc2 commit 8c2b259
Show file tree
Hide file tree
Showing 25 changed files with 385 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.zzang.chongdae.data.mapper

import com.zzang.chongdae.data.remote.dto.response.CommentCreatedAtResponse
import com.zzang.chongdae.domain.model.CommentCreatedAt

fun CommentCreatedAtResponse.toDomain(): CommentCreatedAt {
return CommentCreatedAt(
date = this.date.toLocalDate(),
time = this.time.toLocalTime(),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.zzang.chongdae.data.mapper

import com.zzang.chongdae.data.remote.dto.response.CommentResponse
import com.zzang.chongdae.domain.model.Comment

fun CommentResponse.toDomain(): Comment {
return Comment(
content = this.content,
commentCreatedAt = this.commentCreatedAtResponse.toDomain(),
isMine = this.isMine,
isProposer = this.isProposer,
nickname = this.nickname,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package com.zzang.chongdae.data.mapper

import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter

fun String.toLocalDateTime(): LocalDateTime = LocalDateTime.parse(this, DateTimeFormatter.ISO_LOCAL_DATE_TIME)

fun String.toLocalDate(): LocalDate = LocalDate.parse(this, DateTimeFormatter.ISO_LOCAL_DATE)

fun String.toLocalTime(): LocalTime = LocalTime.parse(this, DateTimeFormatter.ISO_LOCAL_TIME)
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.zzang.chongdae.data.remote.api

import com.zzang.chongdae.data.remote.dto.request.CommentRequest
import com.zzang.chongdae.data.remote.dto.response.CommentsResponse
import com.zzang.chongdae.data.remote.dto.response.MeetingsResponse
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query

interface CommentDetailApiService {
@GET("/offerings/{offering-id}/meetings")
Expand All @@ -18,4 +20,10 @@ interface CommentDetailApiService {
suspend fun postComment(
@Body commentRequest: CommentRequest,
): Response<Unit>

@GET("/comments/{offering-id}")
suspend fun getComments(
@Path("offering-id") offeringId: Long,
@Query("member-id") memberId: Long,
): Response<CommentsResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.zzang.chongdae.data.remote.dto.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CommentCreatedAtResponse(
@SerialName("date")
val date: String,
@SerialName("time")
val time: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.zzang.chongdae.data.remote.dto.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable()
data class CommentResponse(
@SerialName("content")
val content: String,
@SerialName("createdAt")
val commentCreatedAtResponse: CommentCreatedAtResponse,
@SerialName("isMine")
val isMine: Boolean,
@SerialName("isProposer")
val isProposer: Boolean,
@SerialName("nickname")
val nickname: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.zzang.chongdae.data.remote.dto.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CommentsResponse(
@SerialName("comments")
val commentsResponse: List<CommentResponse>,
)
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.zzang.chongdae.data.remote.source

import com.zzang.chongdae.data.remote.dto.request.CommentRequest
import com.zzang.chongdae.data.remote.dto.response.CommentsResponse
import com.zzang.chongdae.data.remote.dto.response.MeetingsResponse

interface CommentDetailDataSource {
suspend fun getMeetings(offeringId: Long): Result<MeetingsResponse>

suspend fun saveComment(commentRequest: CommentRequest): Result<Unit>

suspend fun fetchComments(
offeringId: Long,
memberId: Long,
): Result<CommentsResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.zzang.chongdae.data.remote.source.impl

import com.zzang.chongdae.data.remote.api.CommentDetailApiService
import com.zzang.chongdae.data.remote.dto.request.CommentRequest
import com.zzang.chongdae.data.remote.dto.response.CommentsResponse
import com.zzang.chongdae.data.remote.dto.response.MeetingsResponse
import com.zzang.chongdae.data.remote.source.CommentDetailDataSource
import retrofit2.Response
Expand All @@ -25,4 +26,17 @@ class CommentDetailDataSourceImpl(
service.postComment(commentRequest = commentRequest)
.body() ?: throw IllegalStateException()
}

override suspend fun fetchComments(
offeringId: Long,
memberId: Long,
): Result<CommentsResponse> =
runCatching {
val response: Response<CommentsResponse> = service.getComments(offeringId, memberId)
if (response.isSuccessful) {
response.body() ?: error("에러 발생: null")
} else {
error("에러 발생: ${response.code()}")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.zzang.chongdae.data.repository.remote
import com.zzang.chongdae.data.mapper.toDomain
import com.zzang.chongdae.data.remote.dto.request.CommentRequest
import com.zzang.chongdae.data.remote.source.CommentDetailDataSource
import com.zzang.chongdae.domain.model.Comment
import com.zzang.chongdae.domain.model.Meetings
import com.zzang.chongdae.domain.repository.CommentDetailRepository

Expand All @@ -23,4 +24,16 @@ class CommentDetailRepositoryImpl(
commentDetailDataSource.saveComment(
commentRequest = CommentRequest(memberId, offeringId, comment),
)

override suspend fun fetchComments(
offeringId: Long,
memberId: Long,
): Result<List<Comment>> {
return commentDetailDataSource.fetchComments(
offeringId,
memberId,
).mapCatching { response ->
response.commentsResponse.map { it.toDomain() }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.zzang.chongdae.domain.model

data class Comment(
val content: String,
val commentCreatedAt: CommentCreatedAt,
val isMine: Boolean,
val isProposer: Boolean,
val nickname: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.zzang.chongdae.domain.model

import java.time.LocalDate
import java.time.LocalTime

data class CommentCreatedAt(
val date: LocalDate,
val time: LocalTime,
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.zzang.chongdae.domain.repository

import com.zzang.chongdae.domain.model.Comment
import com.zzang.chongdae.domain.model.Meetings

interface CommentDetailRepository {
Expand All @@ -10,4 +11,9 @@ interface CommentDetailRepository {
offeringId: Long,
comment: String,
): Result<Unit>

suspend fun fetchComments(
offeringId: Long,
memberId: Long,
): Result<List<Comment>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.bumptech.glide.Glide
import com.zzang.chongdae.R
import com.zzang.chongdae.domain.model.OfferingCondition
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.util.Locale
import java.util.regex.Pattern
Expand Down Expand Up @@ -161,3 +162,14 @@ fun setFormattedDeadline(
textView.text = it.format(formatter)
}
}

@BindingAdapter("formattedCommentTime")
fun TextView.setTime(localTime: LocalTime) {
this.text = localTime.format(DateTimeFormatter.ofPattern(context.getString(R.string.amPmTime), Locale.KOREAN))
}

@BindingAdapter("setImageProposer")
fun ImageView.setImageProposer(proposer: Boolean) {
val imageRes = if (proposer) R.drawable.ic_proposer else R.drawable.ic_not_proposer
setImageResource(imageRes)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,45 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.doOnPreDraw
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.zzang.chongdae.R
import com.zzang.chongdae.data.remote.api.NetworkManager
import com.zzang.chongdae.data.remote.source.impl.CommentDetailDataSourceImpl
import com.zzang.chongdae.data.repository.remote.CommentDetailRepositoryImpl
import com.zzang.chongdae.databinding.ActivityCommentDetailBinding
import com.zzang.chongdae.presentation.view.commentdetail.adapter.CommentAdapter

class CommentDetailActivity : AppCompatActivity() {
private var _binding: ActivityCommentDetailBinding? = null
private val binding get() = _binding!!
private val commentAdapter: CommentAdapter by lazy { CommentAdapter() }
private val viewModel: CommentDetailViewModel by viewModels {
CommentDetailViewModel.getFactory(
offeringId,
offeringTitle,
CommentDetailRepositoryImpl(
CommentDetailDataSourceImpl(NetworkManager.commentDetailService()),
),
)
}

private val offeringId by lazy {
intent.getLongExtra(
EXTRA_OFFERING_ID_KEY,
EXTRA_DEFAULT_VALUE,
)
}

private val offeringTitle by lazy {
intent.getStringExtra(EXTRA_OFFERING_TITLE_KEY) ?: DEFAULT_OFFERING_TITLE
}

private val viewModel: CommentDetailViewModel by viewModels {
CommentDetailViewModel.getFactory(
offeringId,
offeringTitle,
CommentDetailRepositoryImpl(
CommentDetailDataSourceImpl(NetworkManager.commentDetailService()),
),
)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initBinding()
initAdapter()
setUpCommentsObserve()
}

override fun onDestroy() {
Expand All @@ -54,6 +57,26 @@ class CommentDetailActivity : AppCompatActivity() {
binding.lifecycleOwner = this
}

private fun initAdapter() {
binding.rvComments.apply {
adapter = commentAdapter
layoutManager =
LinearLayoutManager(this@CommentDetailActivity).apply {
stackFromEnd = true
}
}
}

private fun setUpCommentsObserve() {
viewModel.comments.observe(this) { comments ->
commentAdapter.submitList(comments) {
binding.rvComments.doOnPreDraw {
binding.rvComments.scrollToPosition(comments.size - 1)
}
}
}
}

companion object {
private const val EXTRA_DEFAULT_VALUE = 1L
private const val EXTRA_OFFERING_ID_KEY = "offering_id_key"
Expand Down
Loading

0 comments on commit 8c2b259

Please sign in to comment.