mirror of https://github.com/usbharu/Hideout.git
feat: タイムラインにリアクションを表示するように
This commit is contained in:
parent
2b567bb1d5
commit
e42919ce3c
|
@ -29,9 +29,9 @@ sealed class Emoji {
|
||||||
abstract fun id(): String
|
abstract fun id(): String
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Emoji(" +
|
return "Emoji(" +
|
||||||
"domain='$domain', " +
|
"domain='$domain', " +
|
||||||
"name='$name'" +
|
"name='$name'" +
|
||||||
")"
|
")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,10 @@ data class CustomEmoji(
|
||||||
data class UnicodeEmoji(
|
data class UnicodeEmoji(
|
||||||
override val name: String
|
override val name: String
|
||||||
) : Emoji() {
|
) : Emoji() {
|
||||||
override val domain: Domain = Domain("unicode.org")
|
override val domain: Domain = Companion.domain
|
||||||
override fun id(): String = name
|
override fun id(): String = name
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val domain = Domain("unicode.org")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail
|
package dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.model.Reactions
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
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.media.Media
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
|
@ -29,7 +30,8 @@ data class TimelineObjectDetail(
|
||||||
val isPureRepost: Boolean,
|
val isPureRepost: Boolean,
|
||||||
val lastUpdateAt: Instant,
|
val lastUpdateAt: Instant,
|
||||||
val hasMediaInRepost: Boolean,
|
val hasMediaInRepost: Boolean,
|
||||||
val warnFilter: List<TimelineObjectWarnFilter>
|
val warnFilter: List<TimelineObjectWarnFilter>,
|
||||||
|
val reactionsList: List<Reactions>
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
@ -48,7 +50,8 @@ data class TimelineObjectDetail(
|
||||||
repostPostMedias: List<Media>?,
|
repostPostMedias: List<Media>?,
|
||||||
repostPostActor: Actor?,
|
repostPostActor: Actor?,
|
||||||
repostPostActorIconMedia: Media?,
|
repostPostActorIconMedia: Media?,
|
||||||
warnFilter: List<TimelineObjectWarnFilter>
|
warnFilter: List<TimelineObjectWarnFilter>,
|
||||||
|
reactionsList: List<Reactions>
|
||||||
): TimelineObjectDetail {
|
): TimelineObjectDetail {
|
||||||
return TimelineObjectDetail(
|
return TimelineObjectDetail(
|
||||||
id = timelineObject.id,
|
id = timelineObject.id,
|
||||||
|
@ -69,7 +72,8 @@ data class TimelineObjectDetail(
|
||||||
isPureRepost = timelineObject.isPureRepost,
|
isPureRepost = timelineObject.isPureRepost,
|
||||||
lastUpdateAt = timelineObject.lastUpdatedAt,
|
lastUpdateAt = timelineObject.lastUpdatedAt,
|
||||||
hasMediaInRepost = timelineObject.hasMediaInRepost,
|
hasMediaInRepost = timelineObject.hasMediaInRepost,
|
||||||
warnFilter = warnFilter
|
warnFilter = warnFilter,
|
||||||
|
reactionsList = reactionsList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedquery
|
package dev.usbharu.hideout.core.infrastructure.exposedquery
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.application.model.Reactions
|
import dev.usbharu.hideout.core.application.model.Reactions
|
||||||
|
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toCustomEmojiOrNull
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toCustomEmojiOrNull
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
|
||||||
import dev.usbharu.hideout.core.query.reactions.ReactionsQueryService
|
import dev.usbharu.hideout.core.query.reactions.ReactionsQueryService
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.net.URI
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions as ExposedrepositoryReactions
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions as ExposedrepositoryReactions
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
|
@ -33,6 +35,35 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun findAllByPostIdIn(postIds: List<PostId>): List<Reactions> {
|
||||||
|
return query {
|
||||||
|
|
||||||
|
val actorIdsQuery =
|
||||||
|
ExposedrepositoryReactions.actorId.castTo<String>(VarCharColumnType()).groupConcat(",", true)
|
||||||
|
|
||||||
|
ExposedrepositoryReactions.leftJoin(CustomEmojis)
|
||||||
|
.select(
|
||||||
|
ExposedrepositoryReactions.postId,
|
||||||
|
ExposedrepositoryReactions.postId.count(),
|
||||||
|
ExposedrepositoryReactions.customEmojiId.max(),
|
||||||
|
ExposedrepositoryReactions.unicodeEmoji.max(),
|
||||||
|
actorIdsQuery
|
||||||
|
)
|
||||||
|
.where { ExposedrepositoryReactions.postId inList postIds.map { it.id } }
|
||||||
|
.groupBy(ExposedrepositoryReactions.postId)
|
||||||
|
.map {
|
||||||
|
Reactions(
|
||||||
|
it[ExposedrepositoryReactions.postId],
|
||||||
|
it[ExposedrepositoryReactions.postId.count()].toInt(),
|
||||||
|
it.getOrNull(CustomEmojis.name) ?: it[ExposedrepositoryReactions.unicodeEmoji],
|
||||||
|
it.getOrNull(CustomEmojis.domain) ?: UnicodeEmoji.domain.domain,
|
||||||
|
it.getOrNull(CustomEmojis.url)?.let { it1 -> URI.create(it1) },
|
||||||
|
it[actorIdsQuery].split(",").mapNotNull { it.toLongOrNull() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override val logger: Logger
|
override val logger: Logger
|
||||||
get() = Companion.logger
|
get() = Companion.logger
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.timeline
|
package dev.usbharu.hideout.core.infrastructure.timeline
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.model.Reactions
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
||||||
|
@ -243,8 +244,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
val actors =
|
val actors =
|
||||||
getActors(
|
getActors(
|
||||||
timelineObjectList.map { it.postActorId } +
|
timelineObjectList.map { it.postActorId } +
|
||||||
timelineObjectList.mapNotNull { it.repostActorId } +
|
timelineObjectList.mapNotNull { it.repostActorId } +
|
||||||
timelineObjectList.mapNotNull { it.replyActorId }
|
timelineObjectList.mapNotNull { it.replyActorId }
|
||||||
)
|
)
|
||||||
|
|
||||||
val postMap = posts.associate { post ->
|
val postMap = posts.associate { post ->
|
||||||
|
@ -253,9 +254,11 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
|
|
||||||
val mediaMap = getMedias(
|
val mediaMap = getMedias(
|
||||||
posts.flatMap { it.mediaIds } +
|
posts.flatMap { it.mediaIds } +
|
||||||
actors.mapNotNull { it.value.icon }
|
actors.mapNotNull { it.value.icon }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val reactions = getReactions(posts.map { it.id })
|
||||||
|
|
||||||
return PaginationList(
|
return PaginationList(
|
||||||
timelineObjectList.mapNotNull<TimelineObject, TimelineObjectDetail> {
|
timelineObjectList.mapNotNull<TimelineObject, TimelineObjectDetail> {
|
||||||
val timelineUserDetail = userDetails[it.userDetailId] ?: return@mapNotNull null
|
val timelineUserDetail = userDetails[it.userDetailId] ?: return@mapNotNull null
|
||||||
|
@ -268,6 +271,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
val repost = postMap[it.repostId]
|
val repost = postMap[it.repostId]
|
||||||
val repostMedias = repost?.post?.mediaIds?.mapNotNull { mediaId -> mediaMap[mediaId] }
|
val repostMedias = repost?.post?.mediaIds?.mapNotNull { mediaId -> mediaMap[mediaId] }
|
||||||
val repostActor = actors[it.repostActorId]
|
val repostActor = actors[it.repostActorId]
|
||||||
|
val reactionsList = reactions[it.postId].orEmpty()
|
||||||
TimelineObjectDetail.of(
|
TimelineObjectDetail.of(
|
||||||
timelineObject = it,
|
timelineObject = it,
|
||||||
timelineUserDetail = timelineUserDetail,
|
timelineUserDetail = timelineUserDetail,
|
||||||
|
@ -288,7 +292,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
filterResult.filter.id,
|
filterResult.filter.id,
|
||||||
filterResult.matchedKeyword
|
filterResult.matchedKeyword
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
reactionsList = reactionsList
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
timelineObjectList.lastOrNull()?.postId,
|
timelineObjectList.lastOrNull()?.postId,
|
||||||
|
@ -300,5 +305,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
|
|
||||||
protected abstract suspend fun getMedias(mediaIds: List<MediaId>): Map<MediaId, Media>
|
protected abstract suspend fun getMedias(mediaIds: List<MediaId>): Map<MediaId, Media>
|
||||||
|
|
||||||
|
protected abstract suspend fun getReactions(postIds: List<PostId>): Map<PostId, List<Reactions>>
|
||||||
|
|
||||||
protected abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
|
protected abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.timeline
|
package dev.usbharu.hideout.core.infrastructure.timeline
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.model.Reactions
|
||||||
import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig
|
import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
|
@ -32,6 +33,7 @@ import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
|
||||||
import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl
|
import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl
|
||||||
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||||
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
|
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
|
||||||
|
import dev.usbharu.hideout.core.query.reactions.ReactionsQueryService
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
@ -49,7 +51,8 @@ open class DefaultTimelineStore(
|
||||||
private val userDetailRepository: UserDetailRepository,
|
private val userDetailRepository: UserDetailRepository,
|
||||||
private val actorRepository: ActorRepository,
|
private val actorRepository: ActorRepository,
|
||||||
private val mediaRepository: MediaRepository,
|
private val mediaRepository: MediaRepository,
|
||||||
private val postIPostReadAccessControl: IPostReadAccessControl
|
private val postIPostReadAccessControl: IPostReadAccessControl,
|
||||||
|
private val reactionsQueryService: ReactionsQueryService,
|
||||||
) : AbstractTimelineStore(idGenerateService) {
|
) : AbstractTimelineStore(idGenerateService) {
|
||||||
override suspend fun getTimelines(actorId: ActorId): List<Timeline> {
|
override suspend fun getTimelines(actorId: ActorId): List<Timeline> {
|
||||||
return timelineRepository.findByIds(
|
return timelineRepository.findByIds(
|
||||||
|
@ -157,6 +160,10 @@ open class DefaultTimelineStore(
|
||||||
override suspend fun getMedias(mediaIds: List<MediaId>): Map<MediaId, Media> =
|
override suspend fun getMedias(mediaIds: List<MediaId>): Map<MediaId, Media> =
|
||||||
mediaRepository.findByIds(mediaIds).associateBy { it.id }
|
mediaRepository.findByIds(mediaIds).associateBy { it.id }
|
||||||
|
|
||||||
|
override suspend fun getReactions(postIds: List<PostId>): Map<PostId, List<Reactions>> {
|
||||||
|
return reactionsQueryService.findAllByPostIdIn(postIds).groupBy { PostId(it.postId) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail> =
|
override suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail> =
|
||||||
userDetailRepository.findAllById(userDetailIdList).associateBy { it.id }
|
userDetailRepository.findAllById(userDetailIdList).associateBy { it.id }
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,5 @@ import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
|
|
||||||
interface ReactionsQueryService {
|
interface ReactionsQueryService {
|
||||||
suspend fun findAllByPostId(postId: PostId): List<Reactions>
|
suspend fun findAllByPostId(postId: PostId): List<Reactions>
|
||||||
|
suspend fun findAllByPostIdIn(postIds: List<PostId>): List<Reactions>
|
||||||
}
|
}
|
Loading…
Reference in New Issue