feat: Post詳細ページでリアクションを表示できるように

This commit is contained in:
usbharu 2024-09-08 15:58:05 +09:00
parent 1b4dbc8566
commit 2b567bb1d5
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
9 changed files with 117 additions and 14 deletions

View File

@ -0,0 +1,28 @@
package dev.usbharu.hideout.core.application.model
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
import java.net.URI
data class Reactions(
val postId: Long,
val count: Int,
val name: String,
val domain: String,
val url: URI?,
val actorIds: List<Long>,
) {
companion object {
fun of(reactionList: List<Reaction>, customEmoji: CustomEmoji?): Reactions {
val first = reactionList.first()
return Reactions(
first.id.value,
reactionList.size,
customEmoji?.name ?: first.unicodeEmoji.name,
customEmoji?.domain?.domain ?: first.unicodeEmoji.domain.domain,
customEmoji?.url,
reactionList.map { it.actorId.id }
)
}
}
}

View File

@ -11,6 +11,7 @@ import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl
import dev.usbharu.hideout.core.query.reactions.ReactionsQueryService
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
@ -20,7 +21,8 @@ class GetPostDetailApplicationService(
private val postRepository: PostRepository,
private val actorRepository: ActorRepository,
private val mediaRepository: MediaRepository,
private val iPostReadAccessControl: IPostReadAccessControl
private val iPostReadAccessControl: IPostReadAccessControl,
private val reactionsQueryService: ReactionsQueryService,
) : AbstractApplicationService<GetPostDetail, PostDetail>(
transaction,
logger
@ -38,6 +40,8 @@ class GetPostDetailApplicationService(
val mediaList = mediaRepository.findByIds(post.mediaIds)
val reactions = reactionsQueryService.findAllByPostId(post.id)
return PostDetail.of(
post = post,
actor = actor,
@ -46,6 +50,7 @@ class GetPostDetailApplicationService(
reply = post.replyId?.let { fetchChild(it, actor, iconMedia, principal) },
repost = post.repostId?.let { fetchChild(it, actor, iconMedia, principal) },
moveTo = post.moveTo?.let { fetchChild(it, actor, iconMedia, principal) },
reactionsList = reactions
)
}
@ -69,10 +74,11 @@ class GetPostDetailApplicationService(
val mediaList = mediaRepository.findByIds(post.mediaIds)
return PostDetail.of(
post,
first,
third,
mediaList
post = post,
actor = first,
iconMedia = third,
mediaList = mediaList,
reactionsList = emptyList()
)
}

View File

@ -1,5 +1,6 @@
package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.model.Reactions
import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.media.Media
import dev.usbharu.hideout.core.domain.model.post.Post
@ -23,7 +24,8 @@ data class PostDetail(
val sensitive: Boolean,
val deleted: Boolean,
val mediaDetailList: List<MediaDetail>,
val moveTo: PostDetail?
val moveTo: PostDetail?,
val reactionsList: List<Reactions>
) {
companion object {
@Suppress("LongParameterList")
@ -35,6 +37,7 @@ data class PostDetail(
reply: PostDetail? = null,
repost: PostDetail? = null,
moveTo: PostDetail? = null,
reactionsList: List<Reactions>
): PostDetail {
return PostDetail(
id = post.id.id,
@ -52,7 +55,8 @@ data class PostDetail(
sensitive = post.sensitive,
deleted = false,
mediaDetailList = mediaList.map { MediaDetail.of(it) },
moveTo = moveTo
moveTo = moveTo,
reactionsList = reactionsList
)
}
}

View File

@ -47,7 +47,8 @@ class ReadTimelineApplicationService(
it.replyPost,
it.replyPostActor!!,
it.replyPostActorIconMedia,
it.replyPostMedias.orEmpty()
it.replyPostMedias.orEmpty(),
reactionsList = emptyList(),
)
} else {
null
@ -56,10 +57,11 @@ class ReadTimelineApplicationService(
val repost = if (it.repostPost != null) {
@Suppress("UnsafeCallOnNullableType")
PostDetail.of(
it.repostPost,
it.repostPostActor!!,
it.repostPostActorIconMedia,
it.repostPostMedias.orEmpty()
post = it.repostPost,
actor = it.repostPostActor!!,
iconMedia = it.repostPostActorIconMedia,
mediaList = it.repostPostMedias.orEmpty(),
reactionsList = emptyList()
)
} else {
null
@ -71,7 +73,8 @@ class ReadTimelineApplicationService(
iconMedia = it.postActorIconMedia,
mediaList = it.postMedias,
reply = reply,
repost = repost
repost = repost,
reactionsList = emptyList()
)
}

View File

@ -1,7 +1,10 @@
package dev.usbharu.hideout.core.domain.model.reaction
import dev.usbharu.hideout.core.domain.model.post.PostId
interface ReactionRepository {
suspend fun save(reaction: Reaction): Reaction
suspend fun findById(reactionId: ReactionId): Reaction?
suspend fun findByPostId(postId: PostId): List<Reaction>
suspend fun delete(reaction: Reaction)
}

View File

@ -0,0 +1,42 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery
import dev.usbharu.hideout.core.application.model.Reactions
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toCustomEmojiOrNull
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
import dev.usbharu.hideout.core.query.reactions.ReactionsQueryService
import org.jetbrains.exposed.sql.selectAll
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions as ExposedrepositoryReactions
@Repository
class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() {
override suspend fun findAllByPostId(postId: PostId): List<Reactions> {
return query {
ExposedrepositoryReactions.leftJoin(CustomEmojis).selectAll()
.where { ExposedrepositoryReactions.postId eq postId.id }
.groupBy {
it[ExposedrepositoryReactions.customEmojiId]?.toString()
?: it[ExposedrepositoryReactions.unicodeEmoji]
}
.map { it.value }
.map {
Reactions.of(
it.map { resultRow -> resultRow.toReaction() },
it.first().toCustomEmojiOrNull()
)
}
}
}
override val logger: Logger
get() = Companion.logger
companion object {
private val logger = LoggerFactory.getLogger(ExposedReactionsQueryService::class.java)
}
}

View File

@ -100,7 +100,8 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi
sensitive = it[authorizedQuery[Posts.sensitive]],
deleted = it[authorizedQuery[Posts.deleted]],
mediaDetailList = emptyList(),
moveTo = null
moveTo = null,
emptyList()
)
}

View File

@ -48,6 +48,14 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu
}
}
override suspend fun findByPostId(postId: PostId): List<Reaction> {
return query {
Reactions.selectAll().where {
Reactions.postId eq postId.id
}.map { it.toReaction() }
}
}
override suspend fun delete(reaction: Reaction) {
return query {
Reactions.deleteWhere {

View File

@ -0,0 +1,8 @@
package dev.usbharu.hideout.core.query.reactions
import dev.usbharu.hideout.core.application.model.Reactions
import dev.usbharu.hideout.core.domain.model.post.PostId
interface ReactionsQueryService {
suspend fun findAllByPostId(postId: PostId): List<Reactions>
}