From 72c9b8b7c508e4ddc4f03d93c4d3ed4eb971afe1 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 00:58:18 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20Reaction=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/event/reaction/ReactionEvent.kt | 22 +++++++++ .../hideout/core/domain/model/actor/Actor.kt | 4 +- .../core/domain/model/emoji/CustomEmoji.kt | 2 +- .../emoji/{EmojiId.kt => CustomEmojiId.kt} | 2 +- .../hideout/core/domain/model/post/Post.kt | 6 +-- .../core/domain/model/post/PostContent.kt | 4 +- .../core/domain/model/reaction/Reaction.kt | 49 +++++++++++++++++++ .../core/domain/model/reaction/ReactionId.kt | 4 ++ .../model/reaction/ReactionRepository.kt | 7 +++ .../model/timelineobject/TimelineObject.kt | 6 +-- .../exposed/ActorResultRowMapper.kt | 4 +- .../infrastructure/exposed/PostQueryMapper.kt | 4 +- .../CustomEmojiRepositoryImpl.kt | 6 +-- .../MongoInternalTimelineObjectRepository.kt | 4 +- .../domain/model/actor/TestActorFactory.kt | 4 +- .../{EmojiIdTest.kt => CustomEmojiIdTest.kt} | 6 +-- .../core/domain/model/post/PostTest.kt | 16 +++--- 17 files changed, 116 insertions(+), 34 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/{EmojiId.kt => CustomEmojiId.kt} (93%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt rename hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/emoji/{EmojiIdTest.kt => CustomEmojiIdTest.kt} (82%) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt new file mode 100644 index 00000000..7e89361e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.core.domain.event.reaction + +import dev.usbharu.hideout.core.domain.model.reaction.Reaction +import dev.usbharu.hideout.core.domain.model.reaction.ReactionId +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody + +class ReactionEventFactory(private val reaction: Reaction) { + fun createEvent(reactionEvent: ReactionEvent): DomainEvent = + DomainEvent.create(reactionEvent.eventName, ReactionEventBody(reaction)) +} + +class ReactionEventBody( + reaction: Reaction +) : DomainEventBody(mapOf("reactionId" to reaction.id)) { + fun getReactionId(): ReactionId = toMap()["reactionId"] as ReactionId +} + +enum class ReactionEvent(val eventName: String) { + CREATE("ReactionCreate"), + DELETE("ReactionDelete"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 3b282c34..8fd41710 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -18,7 +18,7 @@ package dev.usbharu.hideout.core.domain.model.actor import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.* -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.instance.InstanceId import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.support.domain.Domain @@ -52,7 +52,7 @@ class Actor( var lastUpdateAt: Instant = createdAt, alsoKnownAs: Set = emptySet(), moveTo: ActorId? = null, - emojiIds: Set, + emojiIds: Set, deleted: Boolean, icon: MediaId?, banner: MediaId?, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt index e0e29b17..ed82d05b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt @@ -36,7 +36,7 @@ sealed class Emoji { } data class CustomEmoji( - val id: EmojiId, + val id: CustomEmojiId, override val name: String, override val domain: Domain, val instanceId: InstanceId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiId.kt similarity index 93% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiId.kt index 5cf4284d..6ad2ec03 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiId.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.domain.model.emoji @JvmInline -value class EmojiId(val emojiId: Long) { +value class CustomEmojiId(val emojiId: Long) { init { require(0 <= emojiId) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index b953bd8e..db5f2732 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory import dev.usbharu.hideout.core.domain.event.post.PostEvent 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.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.instance.InstanceId import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable @@ -138,7 +138,7 @@ class Post( return content.text } - val emojiIds: List + val emojiIds: List get() { if (hide) { return PostContent.empty.emojiIds @@ -217,7 +217,7 @@ class Post( override fun hashCode(): Int = id.hashCode() - fun reconstructWith(mediaIds: List, emojis: List, visibleActors: Set): Post { + fun reconstructWith(mediaIds: List, emojis: List, visibleActors: Set): Post { return Post( id = id, actorId = actorId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt index 2d787e75..9f6b2db7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -16,9 +16,9 @@ package dev.usbharu.hideout.core.domain.model.post -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId -data class PostContent(val text: String, val content: String, val emojiIds: List) { +data class PostContent(val text: String, val content: String, val emojiIds: List) { companion object { val empty = PostContent("", "", emptyList()) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt new file mode 100644 index 00000000..ea3eac76 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt @@ -0,0 +1,49 @@ +package dev.usbharu.hideout.core.domain.model.reaction + +import dev.usbharu.hideout.core.domain.event.reaction.ReactionEvent +import dev.usbharu.hideout.core.domain.event.reaction.ReactionEventFactory +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId +import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable +import java.time.Instant + +class Reaction( + val id: ReactionId, + val postId: PostId, + val actorId: ActorId, + val customEmojiId: CustomEmojiId?, + val unicodeEmoji: UnicodeEmoji, + val createdAt: Instant +) : DomainEventStorable() { + + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Reaction + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + companion object { + fun create( + id: ReactionId, + postId: PostId, + actorId: ActorId, + customEmojiId: CustomEmojiId?, + unicodeEmoji: UnicodeEmoji, + createdAt: Instant + ): Reaction { + return Reaction( + id, postId, actorId, customEmojiId, unicodeEmoji, createdAt + ).apply { addDomainEvent(ReactionEventFactory(this).createEvent(ReactionEvent.CREATE)) } + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt new file mode 100644 index 00000000..af369ff0 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt @@ -0,0 +1,4 @@ +package dev.usbharu.hideout.core.domain.model.reaction + +@JvmInline +value class ReactionId(val value: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt new file mode 100644 index 00000000..ad59e939 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.domain.model.reaction + +interface ReactionRepository { + suspend fun save(reaction: Reaction): Reaction + suspend fun findById(reactionId: String): Reaction? + suspend fun delete(reaction: Reaction) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt index bdce1a0d..73d3e87d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt @@ -1,7 +1,7 @@ package dev.usbharu.hideout.core.domain.model.timelineobject import dev.usbharu.hideout.core.domain.model.actor.ActorId -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.filter.FilterResult import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.Post @@ -28,13 +28,13 @@ class TimelineObject( visibility: Visibility, isPureRepost: Boolean, mediaIds: List, - emojiIds: List, + emojiIds: List, visibleActors: List, hasMediaInRepost: Boolean, lastUpdatedAt: Instant, var warnFilters: List, -) { + ) { var isPureRepost = isPureRepost private set var visibleActors = visibleActors diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt index 59cb72fc..c552c541 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.infrastructure.exposed import dev.usbharu.hideout.core.domain.model.actor.* -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.instance.InstanceId import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.support.domain.Domain @@ -57,7 +57,7 @@ class ActorResultRowMapper : ResultRowMapper { emojiIds = resultRow[Actors.emojis] .split(",") .filter { it.isNotEmpty() } - .map { EmojiId(it.toLong()) } + .map { CustomEmojiId(it.toLong()) } .toSet(), deleted = resultRow[Actors.deleted], icon = resultRow[Actors.icon]?.let { MediaId(it) }, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt index 653533ed..139bd86c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.infrastructure.exposed import dev.usbharu.hideout.core.domain.model.actor.ActorId -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts @@ -55,7 +55,7 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : .mapNotNull { resultRow: ResultRow -> resultRow .getOrNull(PostsEmojis.emojiId) - ?.let { emojiId -> EmojiId(emojiId) } + ?.let { emojiId -> CustomEmojiId(emojiId) } }, visibleActors = it.mapNotNull { resultRow: ResultRow -> resultRow diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt index c095ae55..e3ca9079 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt @@ -17,8 +17,8 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId import dev.usbharu.hideout.core.domain.model.instance.InstanceId import dev.usbharu.hideout.core.domain.model.support.domain.Domain import org.jetbrains.exposed.sql.* @@ -81,7 +81,7 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository, } fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji( - id = EmojiId(this[CustomEmojis.id]), + id = CustomEmojiId(this[CustomEmojis.id]), name = this[CustomEmojis.name], domain = Domain(this[CustomEmojis.domain]), instanceId = InstanceId(this[CustomEmojis.instanceId]), @@ -92,7 +92,7 @@ fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji( fun ResultRow.toCustomEmojiOrNull(): CustomEmoji? { return CustomEmoji( - id = EmojiId(this.getOrNull(CustomEmojis.id) ?: return null), + id = CustomEmojiId(this.getOrNull(CustomEmojis.id) ?: return null), name = this.getOrNull(CustomEmojis.name) ?: return null, domain = Domain(this.getOrNull(CustomEmojis.domain) ?: return null), instanceId = InstanceId(this.getOrNull(CustomEmojis.instanceId) ?: return null), diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt index 1e476d0c..c0cf2472 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt @@ -1,7 +1,7 @@ package dev.usbharu.hideout.core.infrastructure.mongorepository import dev.usbharu.hideout.core.domain.model.actor.ActorId -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.PostId @@ -151,7 +151,7 @@ data class SpringDataMongoTimelineObject( visibility = visibility, isPureRepost = isPureRepost, mediaIds = mediaIds.map { MediaId(it) }, - emojiIds = emojiIds.map { EmojiId(it) }, + emojiIds = emojiIds.map { CustomEmojiId(it) }, visibleActors = visibleActors.map { ActorId(it) }, hasMediaInRepost = hasMediaInRepost, lastUpdatedAt = Instant.ofEpochSecond(lastUpdatedAt), diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt index 3286ed74..4f81a194 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt @@ -1,6 +1,6 @@ package dev.usbharu.hideout.core.domain.model.actor -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.instance.InstanceId import dev.usbharu.hideout.core.domain.model.support.domain.Domain import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService @@ -35,7 +35,7 @@ object TestActorFactory { suspend: Boolean = false, alsoKnownAs: Set = emptySet(), moveTo: Long? = null, - emojiIds: Set = emptySet(), + emojiIds: Set = emptySet(), deleted: Boolean = false, roles: Set = emptySet(), ): Actor { diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiIdTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiIdTest.kt similarity index 82% rename from hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiIdTest.kt rename to hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiIdTest.kt index c304294b..e9383cdb 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiIdTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiIdTest.kt @@ -3,18 +3,18 @@ package dev.usbharu.hideout.core.domain.model.emoji import org.junit.jupiter.api.Assertions.assertDoesNotThrow import org.junit.jupiter.api.Test -class EmojiIdTest { +class CustomEmojiIdTest { @Test fun emojiIdは0以上である必要がある() { org.junit.jupiter.api.assertThrows { - EmojiId(-1) + CustomEmojiId(-1) } } @Test fun emojiIdは0以上なら設定できる() { assertDoesNotThrow { - EmojiId(1) + CustomEmojiId(1) } } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index ae8cc674..a07a0b0d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -4,7 +4,7 @@ import dev.usbharu.hideout.core.domain.event.post.PostEvent import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId import dev.usbharu.hideout.core.domain.model.media.MediaId import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -447,7 +447,7 @@ class PostTest { @Test fun `emojiIds hideがtrueの時empty`() { val actor = TestActorFactory.create() - val emojiIds = listOf(EmojiId(1), EmojiId(2)) + val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2)) val post = Post.create( id = PostId(1), actorId = actor.id, @@ -473,7 +473,7 @@ class PostTest { @Test fun `emojiIds hideがfalseの時中身が返される`() { val actor = TestActorFactory.create() - val emojiIds = listOf(EmojiId(1), EmojiId(2)) + val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2)) val post = Post.create( id = PostId(1), actorId = actor.id, @@ -500,7 +500,7 @@ class PostTest { val post = TestPostFactory.create() val mediaIds = listOf(MediaId(1)) val visibleActors = setOf((ActorId(2))) - val emojis = listOf(EmojiId(3)) + val emojis = listOf(CustomEmojiId(3)) val reconstructWith = post.reconstructWith(mediaIds, emojis, visibleActors) assertEquals(mediaIds, reconstructWith.mediaIds) @@ -511,7 +511,7 @@ class PostTest { @Test fun `mediaIds hideがtrueの時emptyが返される`() { val actor = TestActorFactory.create() - val emojiIds = listOf(EmojiId(1), EmojiId(2)) + val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2)) val mediaIds = listOf(MediaId(1)) val post = Post.create( id = PostId(1), @@ -538,7 +538,7 @@ class PostTest { @Test fun `mediaIds hideがfalseの時中身が返される`() { val actor = TestActorFactory.create() - val emojiIds = listOf(EmojiId(1), EmojiId(2)) + val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2)) val mediaIds = listOf(MediaId(2)) val post = Post.create( id = PostId(1), @@ -603,7 +603,7 @@ class PostTest { fun `restore 指定された引数で再構成されCHECKUPDATEイベントが発生する`() { val post = TestPostFactory.create(deleted = true) - val postContent = PostContent("aiueo", "aiueo", listOf(EmojiId(1))) + val postContent = PostContent("aiueo", "aiueo", listOf(CustomEmojiId(1))) val overview = PostOverview("overview") val mediaIds = listOf(MediaId(1)) post.restore( @@ -622,7 +622,7 @@ class PostTest { fun deletedがfalseの時失敗する() { val post = TestPostFactory.create(deleted = false) - val postContent = PostContent("aiueo", "aiueo", listOf(EmojiId(1))) + val postContent = PostContent("aiueo", "aiueo", listOf(CustomEmojiId(1))) val overview = PostOverview("overview") val mediaIds = listOf(MediaId(1)) assertThrows { From daf676503d3d62c8c9d3585eb33da2b5089d0a57 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 01:10:12 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20ReactionRepository=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/reaction/ReactionRepository.kt | 2 +- .../ExposedReactionRepository.kt | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index ad59e939..83ac26f9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -2,6 +2,6 @@ package dev.usbharu.hideout.core.domain.model.reaction interface ReactionRepository { suspend fun save(reaction: Reaction): Reaction - suspend fun findById(reactionId: String): Reaction? + suspend fun findById(reactionId: ReactionId): Reaction? suspend fun delete(reaction: Reaction) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt new file mode 100644 index 00000000..1d1d7564 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt @@ -0,0 +1,87 @@ +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId +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.reaction.Reaction +import dev.usbharu.hideout.core.domain.model.reaction.ReactionId +import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher +import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.javatime.timestamp +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Repository + +@Repository +class ExposedReactionRepository(override val domainEventPublisher: DomainEventPublisher) : ReactionRepository, + AbstractRepository(), DomainEventPublishableRepository { + + override val logger: Logger + get() = Companion.logger + + + override suspend fun save(reaction: Reaction): Reaction { + return query { + Reactions.upsert { + it[Reactions.id] = reaction.id.value + it[Reactions.postId] = reaction.postId.id + it[Reactions.actorId] = reaction.actorId.id + it[Reactions.customEmojiId] = reaction.customEmojiId?.emojiId + it[Reactions.unicodeEmoji] = reaction.unicodeEmoji.name + } + onComplete { + update(reaction) + } + reaction + } + } + + override suspend fun findById(reactionId: ReactionId): Reaction? { + return query { + Reactions.selectAll().where { + Reactions.id eq reactionId.value + }.singleOrNull()?.toReaction() + } + } + + override suspend fun delete(reaction: Reaction) { + return query { + Reactions.deleteWhere { + Reactions.id eq reaction.id.value + } + onComplete { + update(reaction) + } + } + } + + companion object { + private val logger = LoggerFactory.getLogger(ExposedReactionRepository::class.java) + } +} + +fun ResultRow.toReaction(): Reaction { + return Reaction( + ReactionId(this[Reactions.id]), + PostId(this[Reactions.postId]), + ActorId(this[Reactions.actorId]), + this[Reactions.customEmojiId]?.let { CustomEmojiId(it) }, + UnicodeEmoji(this[Reactions.unicodeEmoji]), + this[Reactions.createdAt] + ) + +} + +object Reactions : Table("Reactions") { + val id = long("id") + val postId = long("post_id").references(Posts.id) + val actorId = long("actor_id").references(Actors.id) + val customEmojiId = long("custom_emoji_id").references(CustomEmojis.id).nullable() + val unicodeEmoji = varchar("unicode_emoji", 100) + val createdAt = timestamp("created_at") + override val primaryKey: PrimaryKey = PrimaryKey(id) +} \ No newline at end of file From 1b4dbc8566df643d3e1ef148e9e7abccb4f8526c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 01:17:58 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20reaction=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=83=BC=E3=83=96=E3=83=AB=E5=AE=9A=E7=BE=A9=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposedrepository/ExposedReactionRepository.kt | 2 +- .../main/resources/db/migration/V1__Init_DB.sql | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt index 1d1d7564..92c8ebd9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt @@ -76,7 +76,7 @@ fun ResultRow.toReaction(): Reaction { } -object Reactions : Table("Reactions") { +object Reactions : Table("reactions") { val id = long("id") val postId = long("post_id").references(Posts.id) val actorId = long("actor_id").references(Actors.id) diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 6ce43568..44bfbcf5 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -318,4 +318,16 @@ create table if not exists filter_keywords keyword varchar(1000) not null, mode varchar(100) not null, constraint fk_filter_keywords_filter_id__id foreign key (filter_id) references filters (id) on delete cascade on update cascade -) \ No newline at end of file +); + +create table if not exists reactions +( + id bigint primary key, + post_id bigint not null, + actor_id bigint not null, + custom_emoji_id bigint null, + unicode_emoji varchar(100) not null, + created_at timestamp not null, + constraint fk_reactions_post_id__id foreign key (post_id) references posts (id) on delete cascade on update cascade, + constraint fk_reactions_actor_id__id foreign key (actor_id) references actors (id) on delete cascade on update cascade +); \ No newline at end of file From 2b567bb1d5fe88e9d2cddcf33b3bf4eb28297d0f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 15:58:05 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20Post=E8=A9=B3=E7=B4=B0=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=81=A7=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/application/model/Reactions.kt | 28 +++++++++++++ .../post/GetPostDetailApplicationService.kt | 16 ++++--- .../core/application/post/PostDetail.kt | 8 +++- .../ReadTimelineApplicationService.kt | 15 ++++--- .../model/reaction/ReactionRepository.kt | 3 ++ .../ExposedReactionsQueryService.kt | 42 +++++++++++++++++++ .../ExposedUserTimelineQueryService.kt | 3 +- .../ExposedReactionRepository.kt | 8 ++++ .../query/reactions/ReactionsQueryService.kt | 8 ++++ 9 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/model/Reactions.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/model/Reactions.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/model/Reactions.kt new file mode 100644 index 00000000..11d95f7e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/model/Reactions.kt @@ -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, +) { + companion object { + fun of(reactionList: List, 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 } + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt index 7eb38ed3..a9abd30a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt @@ -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( 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() ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt index 567848df..3cee2e7c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt @@ -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, - val moveTo: PostDetail? + val moveTo: PostDetail?, + val reactionsList: List ) { companion object { @Suppress("LongParameterList") @@ -35,6 +37,7 @@ data class PostDetail( reply: PostDetail? = null, repost: PostDetail? = null, moveTo: PostDetail? = null, + reactionsList: List ): 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 ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt index 9facf26c..129aae7a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt @@ -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() ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index 83ac26f9..29acd189 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -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 suspend fun delete(reaction: Reaction) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt new file mode 100644 index 00000000..89d266b9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt @@ -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 { + 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) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt index 82be6f96..99c5cee4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt @@ -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() ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt index 92c8ebd9..0b376bd0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt @@ -48,6 +48,14 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu } } + override suspend fun findByPostId(postId: PostId): List { + return query { + Reactions.selectAll().where { + Reactions.postId eq postId.id + }.map { it.toReaction() } + } + } + override suspend fun delete(reaction: Reaction) { return query { Reactions.deleteWhere { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt new file mode 100644 index 00000000..a9c78909 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt @@ -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 +} \ No newline at end of file From e42919ce3cd4560724aa92589c1c29ede22b4c62 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 16:40:51 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AB=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/emoji/CustomEmoji.kt | 12 ++++--- .../TimelineObjectDetail.kt | 10 ++++-- .../ExposedReactionsQueryService.kt | 33 ++++++++++++++++++- .../timeline/AbstractTimelineStore.kt | 15 ++++++--- .../timeline/DefaultTimelineStore.kt | 9 ++++- .../query/reactions/ReactionsQueryService.kt | 1 + 6 files changed, 67 insertions(+), 13 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt index ed82d05b..5a64a948 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt @@ -29,9 +29,9 @@ sealed class Emoji { abstract fun id(): String override fun toString(): String { return "Emoji(" + - "domain='$domain', " + - "name='$name'" + - ")" + "domain='$domain', " + + "name='$name'" + + ")" } } @@ -50,6 +50,10 @@ data class CustomEmoji( data class UnicodeEmoji( override val name: String ) : Emoji() { - override val domain: Domain = Domain("unicode.org") + override val domain: Domain = Companion.domain override fun id(): String = name + + companion object { + val domain = Domain("unicode.org") + } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt index 830612dc..a0727f4f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt @@ -1,5 +1,6 @@ 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.media.Media import dev.usbharu.hideout.core.domain.model.post.Post @@ -29,7 +30,8 @@ data class TimelineObjectDetail( val isPureRepost: Boolean, val lastUpdateAt: Instant, val hasMediaInRepost: Boolean, - val warnFilter: List + val warnFilter: List, + val reactionsList: List ) { companion object { @Suppress("LongParameterList") @@ -48,7 +50,8 @@ data class TimelineObjectDetail( repostPostMedias: List?, repostPostActor: Actor?, repostPostActorIconMedia: Media?, - warnFilter: List + warnFilter: List, + reactionsList: List ): TimelineObjectDetail { return TimelineObjectDetail( id = timelineObject.id, @@ -69,7 +72,8 @@ data class TimelineObjectDetail( isPureRepost = timelineObject.isPureRepost, lastUpdateAt = timelineObject.lastUpdatedAt, hasMediaInRepost = timelineObject.hasMediaInRepost, - warnFilter = warnFilter + warnFilter = warnFilter, + reactionsList = reactionsList ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt index 89d266b9..4a779905 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt @@ -1,16 +1,18 @@ package dev.usbharu.hideout.core.infrastructure.exposedquery 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.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.jetbrains.exposed.sql.* import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository +import java.net.URI import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions as ExposedrepositoryReactions @Repository @@ -33,6 +35,35 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() } } + override suspend fun findAllByPostIdIn(postIds: List): List { + return query { + + val actorIdsQuery = + ExposedrepositoryReactions.actorId.castTo(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 get() = Companion.logger diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt index 50801dc2..9032a2ac 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt @@ -1,5 +1,6 @@ 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.ActorId import dev.usbharu.hideout.core.domain.model.filter.Filter @@ -243,8 +244,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe val actors = getActors( timelineObjectList.map { it.postActorId } + - timelineObjectList.mapNotNull { it.repostActorId } + - timelineObjectList.mapNotNull { it.replyActorId } + timelineObjectList.mapNotNull { it.repostActorId } + + timelineObjectList.mapNotNull { it.replyActorId } ) val postMap = posts.associate { post -> @@ -253,9 +254,11 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe val mediaMap = getMedias( posts.flatMap { it.mediaIds } + - actors.mapNotNull { it.value.icon } + actors.mapNotNull { it.value.icon } ) + val reactions = getReactions(posts.map { it.id }) + return PaginationList( timelineObjectList.mapNotNull { 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 repostMedias = repost?.post?.mediaIds?.mapNotNull { mediaId -> mediaMap[mediaId] } val repostActor = actors[it.repostActorId] + val reactionsList = reactions[it.postId].orEmpty() TimelineObjectDetail.of( timelineObject = it, timelineUserDetail = timelineUserDetail, @@ -288,7 +292,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe filterResult.filter.id, filterResult.matchedKeyword ) - } + }, + reactionsList = reactionsList ) }, timelineObjectList.lastOrNull()?.postId, @@ -300,5 +305,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe protected abstract suspend fun getMedias(mediaIds: List): Map + protected abstract suspend fun getReactions(postIds: List): Map> + protected abstract suspend fun getUserDetails(userDetailIdList: List): Map } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt index 36faa561..7426d1b6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt @@ -1,5 +1,6 @@ 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.domain.model.actor.Actor 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.shared.id.IdGenerateService import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption +import dev.usbharu.hideout.core.query.reactions.ReactionsQueryService import org.springframework.stereotype.Component import java.time.Instant @@ -49,7 +51,8 @@ open class DefaultTimelineStore( private val userDetailRepository: UserDetailRepository, private val actorRepository: ActorRepository, private val mediaRepository: MediaRepository, - private val postIPostReadAccessControl: IPostReadAccessControl + private val postIPostReadAccessControl: IPostReadAccessControl, + private val reactionsQueryService: ReactionsQueryService, ) : AbstractTimelineStore(idGenerateService) { override suspend fun getTimelines(actorId: ActorId): List { return timelineRepository.findByIds( @@ -157,6 +160,10 @@ open class DefaultTimelineStore( override suspend fun getMedias(mediaIds: List): Map = mediaRepository.findByIds(mediaIds).associateBy { it.id } + override suspend fun getReactions(postIds: List): Map> { + return reactionsQueryService.findAllByPostIdIn(postIds).groupBy { PostId(it.postId) } + } + override suspend fun getUserDetails(userDetailIdList: List): Map = userDetailRepository.findAllById(userDetailIdList).associateBy { it.id } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt index a9c78909..42164bc0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt @@ -5,4 +5,5 @@ import dev.usbharu.hideout.core.domain.model.post.PostId interface ReactionsQueryService { suspend fun findAllByPostId(postId: PostId): List + suspend fun findAllByPostIdIn(postIds: List): List } \ No newline at end of file From d4aaad3fb669a4e63087af51d8bd7dafc3a0e3a3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 22:26:50 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=97=E3=81=9F=E3=81=8B=E3=82=92?= =?UTF-8?q?=E7=A2=BA=E8=AA=8D=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/GetPostDetailApplicationService.kt | 15 ++++- .../core/application/post/PostDetail.kt | 9 ++- .../application/reaction/CreateReaction.kt | 3 + .../UserCreateReactionApplicationService.kt | 65 +++++++++++++++++++ .../ReadTimelineApplicationService.kt | 7 +- .../model/reaction/ReactionRepository.kt | 9 +++ .../TimelineObjectDetail.kt | 9 ++- .../model/timelineobject/TimelineObject.kt | 28 ++++---- .../service/emoji/UnicodeEmojiService.kt | 5 ++ .../emojikt/EmojiKtUnicodeEmojiService.kt | 12 ++++ .../ExposedReactionsQueryService.kt | 5 +- .../ExposedUserTimelineQueryService.kt | 17 +++-- .../ExposedReactionRepository.kt | 15 +++++ .../MongoInternalTimelineObjectRepository.kt | 9 ++- .../timeline/AbstractTimelineStore.kt | 9 ++- .../interfaces/web/posts/PostsController.kt | 19 +++++- .../resources/templates/fragments-post.html | 12 ++-- .../emojikt/EmojiKtUnicodeEmojiServiceTest.kt | 16 +++++ libs.versions.toml | 2 +- 19 files changed, 224 insertions(+), 42 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/CreateReaction.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiServiceTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt index a9abd30a..278c0afe 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostDetailApplicationService.kt @@ -9,6 +9,7 @@ import dev.usbharu.hideout.core.domain.model.media.Media import dev.usbharu.hideout.core.domain.model.media.MediaRepository 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.reaction.ReactionRepository 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 @@ -23,6 +24,7 @@ class GetPostDetailApplicationService( private val mediaRepository: MediaRepository, private val iPostReadAccessControl: IPostReadAccessControl, private val reactionsQueryService: ReactionsQueryService, + private val reactionRepository: ReactionRepository, ) : AbstractApplicationService( transaction, logger @@ -42,6 +44,13 @@ class GetPostDetailApplicationService( val reactions = reactionsQueryService.findAllByPostId(post.id) + val favourited = reactionRepository.existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji( + post.id, + principal.actorId, + null, + "❤" + ) + return PostDetail.of( post = post, actor = actor, @@ -50,7 +59,8 @@ 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 + reactionsList = reactions, + favourited ) } @@ -78,7 +88,8 @@ class GetPostDetailApplicationService( actor = first, iconMedia = third, mediaList = mediaList, - reactionsList = emptyList() + reactionsList = emptyList(), + favourited = false ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt index 3cee2e7c..67dd9d96 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/PostDetail.kt @@ -25,7 +25,8 @@ data class PostDetail( val deleted: Boolean, val mediaDetailList: List, val moveTo: PostDetail?, - val reactionsList: List + val reactionsList: List, + val favourited: Boolean ) { companion object { @Suppress("LongParameterList") @@ -37,7 +38,8 @@ data class PostDetail( reply: PostDetail? = null, repost: PostDetail? = null, moveTo: PostDetail? = null, - reactionsList: List + reactionsList: List, + favourited: Boolean ): PostDetail { return PostDetail( id = post.id.id, @@ -56,7 +58,8 @@ data class PostDetail( deleted = false, mediaDetailList = mediaList.map { MediaDetail.of(it) }, moveTo = moveTo, - reactionsList = reactionsList + reactionsList = reactionsList, + favourited = favourited ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/CreateReaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/CreateReaction.kt new file mode 100644 index 00000000..7d6f171b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/CreateReaction.kt @@ -0,0 +1,3 @@ +package dev.usbharu.hideout.core.application.reaction + +data class CreateReaction(val postId: Long, val customEmojiId: Long?, val unicodeEmoji: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt new file mode 100644 index 00000000..1ca68aef --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt @@ -0,0 +1,65 @@ +package dev.usbharu.hideout.core.application.reaction + +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService +import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +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.PostRepository +import dev.usbharu.hideout.core.domain.model.reaction.Reaction +import dev.usbharu.hideout.core.domain.model.reaction.ReactionId +import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository +import dev.usbharu.hideout.core.domain.model.support.principal.LocalUser +import dev.usbharu.hideout.core.domain.service.emoji.UnicodeEmojiService +import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.time.Instant + +@Service +class UserCreateReactionApplicationService( + transaction: Transaction, + private val idGenerateService: IdGenerateService, + private val reactionRepository: ReactionRepository, + private val postReadAccessControl: IPostReadAccessControl, + private val postRepository: PostRepository, + private val customEmojiRepository: CustomEmojiRepository, + private val unicodeEmojiService: UnicodeEmojiService +) : + LocalUserAbstractApplicationService( + transaction, logger + ) { + override suspend fun internalExecute(command: CreateReaction, principal: LocalUser) { + + val postId = PostId(command.postId) + val post = postRepository.findById(postId) ?: throw IllegalArgumentException("Post $postId not found.") + if (postReadAccessControl.isAllow(post, principal).not()) { + throw PermissionDeniedException() + } + + val customEmoji = command.customEmojiId?.let { customEmojiRepository.findById(it) } + + val unicodeEmoji = if (unicodeEmojiService.isUnicodeEmoji(command.unicodeEmoji)) { + command.unicodeEmoji + } else { + "❤" + } + + val reaction = Reaction.create( + id = ReactionId(idGenerateService.generateId()), + postId = postId, + actorId = principal.actorId, + customEmojiId = customEmoji?.id, + unicodeEmoji = UnicodeEmoji(unicodeEmoji), + createdAt = Instant.now() + ) + + reactionRepository.save(reaction) + } + + companion object { + private val logger = LoggerFactory.getLogger(UserCreateReactionApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt index 129aae7a..8b045657 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/timeline/ReadTimelineApplicationService.kt @@ -49,6 +49,7 @@ class ReadTimelineApplicationService( it.replyPostActorIconMedia, it.replyPostMedias.orEmpty(), reactionsList = emptyList(), + favourited = false, ) } else { null @@ -61,7 +62,8 @@ class ReadTimelineApplicationService( actor = it.repostPostActor!!, iconMedia = it.repostPostActorIconMedia, mediaList = it.repostPostMedias.orEmpty(), - reactionsList = emptyList() + reactionsList = emptyList(), + favourited = false ) } else { null @@ -74,7 +76,8 @@ class ReadTimelineApplicationService( mediaList = it.postMedias, reply = reply, repost = repost, - reactionsList = emptyList() + reactionsList = emptyList(), + favourited = it.favourited ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index 29acd189..f2ed4779 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -1,10 +1,19 @@ package dev.usbharu.hideout.core.domain.model.reaction +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId 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 + suspend fun existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji( + postId: PostId, + actorId: ActorId, + customEmojiId: CustomEmojiId?, + unicodeEmoji: String + ): Boolean + suspend fun delete(reaction: Reaction) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt index a0727f4f..43b0fa9f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt @@ -31,7 +31,8 @@ data class TimelineObjectDetail( val lastUpdateAt: Instant, val hasMediaInRepost: Boolean, val warnFilter: List, - val reactionsList: List + val reactionsList: List, + val favourited: Boolean ) { companion object { @Suppress("LongParameterList") @@ -51,7 +52,8 @@ data class TimelineObjectDetail( repostPostActor: Actor?, repostPostActorIconMedia: Media?, warnFilter: List, - reactionsList: List + reactionsList: List, + favourited: Boolean ): TimelineObjectDetail { return TimelineObjectDetail( id = timelineObject.id, @@ -73,7 +75,8 @@ data class TimelineObjectDetail( lastUpdateAt = timelineObject.lastUpdatedAt, hasMediaInRepost = timelineObject.hasMediaInRepost, warnFilter = warnFilter, - reactionsList = reactionsList + reactionsList = reactionsList, + favourited = favourited ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt index 73d3e87d..a6fbe671 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt @@ -33,8 +33,8 @@ class TimelineObject( hasMediaInRepost: Boolean, lastUpdatedAt: Instant, var warnFilters: List, - - ) { + var favourited: Boolean +) { var isPureRepost = isPureRepost private set var visibleActors = visibleActors @@ -61,9 +61,9 @@ class TimelineObject( lastUpdatedAt = Instant.now() isPureRepost = post.repostId != null && - post.replyId == null && - post.text.isEmpty() && - post.overview?.overview.isNullOrEmpty() + post.replyId == null && + post.text.isEmpty() && + post.overview?.overview.isNullOrEmpty() warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) } } @@ -82,7 +82,8 @@ class TimelineObject( timeline: Timeline, post: Post, replyActorId: ActorId?, - filterResults: List + filterResults: List, + favourited: Boolean ): TimelineObject { return TimelineObject( id = timelineObjectId, @@ -102,7 +103,8 @@ class TimelineObject( visibleActors = post.visibleActors.toList(), hasMediaInRepost = false, lastUpdatedAt = Instant.now(), - warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) } + warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) }, + favourited = favourited ) } @@ -113,7 +115,8 @@ class TimelineObject( post: Post, replyActorId: ActorId?, repost: Post, - filterResults: List + filterResults: List, + favourited: Boolean ): TimelineObject { require(post.repostId == repost.id) @@ -130,15 +133,16 @@ class TimelineObject( repostActorId = repost.actorId, visibility = post.visibility, isPureRepost = repost.mediaIds.isEmpty() && - repost.overview == null && - repost.content == PostContent.empty && - repost.replyId == null, + repost.overview == null && + repost.content == PostContent.empty && + repost.replyId == null, mediaIds = post.mediaIds, emojiIds = post.emojiIds, visibleActors = post.visibleActors.toList(), hasMediaInRepost = repost.mediaIds.isNotEmpty(), lastUpdatedAt = Instant.now(), - warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) } + warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) }, + favourited = favourited ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt new file mode 100644 index 00000000..add7bdbe --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.domain.service.emoji + +interface UnicodeEmojiService { + fun isUnicodeEmoji(emoji: String): Boolean +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt new file mode 100644 index 00000000..3eef34aa --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt @@ -0,0 +1,12 @@ +package dev.usbharu.hideout.core.infrastructure.emojikt + +import Emojis +import dev.usbharu.hideout.core.domain.service.emoji.UnicodeEmojiService +import org.springframework.stereotype.Service + +@Service +class EmojiKtUnicodeEmojiService : UnicodeEmojiService { + override fun isUnicodeEmoji(emoji: String): Boolean { + return Emojis.allEmojis.singleOrNull { it.char == emoji } != null + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt index 4a779905..b4d7705f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt @@ -46,7 +46,7 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() ExposedrepositoryReactions.postId, ExposedrepositoryReactions.postId.count(), ExposedrepositoryReactions.customEmojiId.max(), - ExposedrepositoryReactions.unicodeEmoji.max(), + ExposedrepositoryReactions.unicodeEmoji.max(), actorIdsQuery ) .where { ExposedrepositoryReactions.postId inList postIds.map { it.id } } @@ -55,7 +55,8 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() Reactions( it[ExposedrepositoryReactions.postId], it[ExposedrepositoryReactions.postId.count()].toInt(), - it.getOrNull(CustomEmojis.name) ?: it[ExposedrepositoryReactions.unicodeEmoji], + it.getOrNull(CustomEmojis.name) + ?: it[ExposedrepositoryReactions.unicodeEmoji.max()]!!, it.getOrNull(CustomEmojis.domain) ?: UnicodeEmoji.domain.domain, it.getOrNull(CustomEmojis.url)?.let { it1 -> URI.create(it1) }, it[actorIdsQuery].split(",").mapNotNull { it.toLongOrNull() } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt index 99c5cee4..6b4338fb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt @@ -42,10 +42,10 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi .select(Posts.columns) .where { Posts.visibility eq Visibility.PUBLIC.name or - (Posts.visibility eq Visibility.UNLISTED.name) or - (Posts.visibility eq Visibility.DIRECT.name and (PostsVisibleActors.actorId eq principal.actorId.id)) or - (Posts.visibility eq Visibility.FOLLOWERS.name and (Relationships.blocking eq false and (relationshipsAlias[Relationships.following] eq true))) or - (Posts.actorId eq principal.actorId.id) + (Posts.visibility eq Visibility.UNLISTED.name) or + (Posts.visibility eq Visibility.DIRECT.name and (PostsVisibleActors.actorId eq principal.actorId.id)) or + (Posts.visibility eq Visibility.FOLLOWERS.name and (Relationships.blocking eq false and (relationshipsAlias[Relationships.following] eq true))) or + (Posts.actorId eq principal.actorId.id) } .alias("authorized_table") } @@ -61,6 +61,10 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi .leftJoin(iconMedia, { Actors.icon }, { iconMedia[Media.id] }) .leftJoin(PostsMedia, { authorizedQuery[Posts.id] }, { PostsMedia.postId }) .leftJoin(Media, { PostsMedia.mediaId }, { Media.id }) + .leftJoin(Reactions, + { authorizedQuery[Posts.id] }, + { Reactions.postId }, + { Reactions.id isDistinctFrom principal.actorId.id }) .selectAll() .where { authorizedQuery[Posts.id] inList idList.map { it.id } } .groupBy { it[authorizedQuery[Posts.id]] } @@ -69,7 +73,7 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi toPostDetail(it.first(), authorizedQuery, iconMedia).copy( mediaDetailList = it.mapNotNull { resultRow -> resultRow.toMediaOrNull()?.let { it1 -> MediaDetail.of(it1) } - } + }, favourited = it.any { it.getOrNull(Reactions.actorId) != null } ) } } @@ -101,7 +105,8 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi deleted = it[authorizedQuery[Posts.deleted]], mediaDetailList = emptyList(), moveTo = null, - emptyList() + emptyList(), + favourited = false ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt index 0b376bd0..f3ac7b96 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt @@ -32,6 +32,7 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu it[Reactions.actorId] = reaction.actorId.id it[Reactions.customEmojiId] = reaction.customEmojiId?.emojiId it[Reactions.unicodeEmoji] = reaction.unicodeEmoji.name + it[Reactions.createdAt] = reaction.createdAt } onComplete { update(reaction) @@ -56,6 +57,20 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu } } + override suspend fun existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji( + postId: PostId, + actorId: ActorId, + customEmojiId: CustomEmojiId?, + unicodeEmoji: String + ): Boolean { + return query { + Reactions.selectAll().where { + Reactions.postId.eq(postId.id).and(Reactions.actorId eq actorId.id) + .and((Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji))) + }.empty().not() + } + } + override suspend fun delete(reaction: Reaction) { return query { Reactions.deleteWhere { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt index c0cf2472..2e66bbbd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt @@ -133,7 +133,8 @@ data class SpringDataMongoTimelineObject( val visibleActors: List, val hasMediaInRepost: Boolean, val lastUpdatedAt: Long, - val warnFilters: List + val warnFilters: List, + val favourited: Boolean ) { fun toTimelineObject(): TimelineObject { @@ -155,7 +156,8 @@ data class SpringDataMongoTimelineObject( visibleActors = visibleActors.map { ActorId(it) }, hasMediaInRepost = hasMediaInRepost, lastUpdatedAt = Instant.ofEpochSecond(lastUpdatedAt), - warnFilters = warnFilters.map { it.toTimelineObjectWarnFilter() } + warnFilters = warnFilters.map { it.toTimelineObjectWarnFilter() }, + favourited = favourited ) } @@ -179,7 +181,8 @@ data class SpringDataMongoTimelineObject( visibleActors = timelineObject.visibleActors.map { it.id }, hasMediaInRepost = timelineObject.hasMediaInRepost, lastUpdatedAt = timelineObject.lastUpdatedAt.epochSecond, - warnFilters = timelineObject.warnFilters.map { SpringDataMongoTimelineObjectWarnFilter.of(it) } + warnFilters = timelineObject.warnFilters.map { SpringDataMongoTimelineObjectWarnFilter.of(it) }, + favourited = timelineObject.favourited ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt index 9032a2ac..9ddcc131 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt @@ -76,7 +76,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe post = post, replyActorId = replyActorId, repost = repost, - filterResults = applyFilters.filterResults + filterResults = applyFilters.filterResults, + favourited = false ) } @@ -85,7 +86,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe timeline = timeline, post = post, replyActorId = replyActorId, - filterResults = applyFilters.filterResults + filterResults = applyFilters.filterResults, + favourited = false ) } @@ -293,7 +295,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe filterResult.matchedKeyword ) }, - reactionsList = reactionsList + reactionsList = reactionsList, + favourited = it.favourited ) }, timelineObjectList.lastOrNull()?.postId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt index 4dda4759..0f6558c6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt @@ -4,18 +4,22 @@ import dev.usbharu.hideout.core.application.exception.PermissionDeniedException import dev.usbharu.hideout.core.application.instance.GetLocalInstanceApplicationService import dev.usbharu.hideout.core.application.post.GetPostDetail import dev.usbharu.hideout.core.application.post.GetPostDetailApplicationService +import dev.usbharu.hideout.core.application.reaction.CreateReaction +import dev.usbharu.hideout.core.application.reaction.UserCreateReactionApplicationService import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder import org.springframework.security.access.AccessDeniedException import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping @Controller class PostsController( private val getPostDetailApplicationService: GetPostDetailApplicationService, private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder, - private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService + private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService, + private val userCreateReactionApplicationService: UserCreateReactionApplicationService ) { @GetMapping("/users/{name}/posts/{id}") suspend fun postById(@PathVariable id: Long, model: Model): String { @@ -31,4 +35,17 @@ class PostsController( return "postById" } + + @PostMapping("/users/{name}/posts/{id}/favourite") + suspend fun favourite(@PathVariable id: Long, @PathVariable name: String): String { + val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal() + userCreateReactionApplicationService.execute( + CreateReaction( + id, + null, + "❤" + ), principal + ) + return "redirect:/users/$name/posts/$id" + } } diff --git a/hideout-core/src/main/resources/templates/fragments-post.html b/hideout-core/src/main/resources/templates/fragments-post.html index e55f55be..671cec83 100644 --- a/hideout-core/src/main/resources/templates/fragments-post.html +++ b/hideout-core/src/main/resources/templates/fragments-post.html @@ -44,10 +44,14 @@
- Reply - - - +
+ Reply + + + + +
diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiServiceTest.kt new file mode 100644 index 00000000..f589be2c --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiServiceTest.kt @@ -0,0 +1,16 @@ +package dev.usbharu.hideout.core.infrastructure.emojikt + +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +class EmojiKtUnicodeEmojiServiceTest { + + @ParameterizedTest + @ValueSource(strings = ["❤", "👱", "☠️", "⁉️"]) + fun 絵文字の判定ができる(s: String) { + assertTrue(EmojiKtUnicodeEmojiService().isUnicodeEmoji(s)) + } + + +} \ No newline at end of file diff --git a/libs.versions.toml b/libs.versions.toml index 88c0adf6..e698a5e4 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -103,7 +103,7 @@ mongodb-kotlin-coroutine = { module = "org.mongodb:mongodb-driver-kotlin-corouti mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "5.4.0" } http-signature = { module = "dev.usbharu:http-signature", version = "1.0.0" } -emoji-kt = { module = "dev.usbharu:emoji-kt", version = "2.0.0" } +emoji-kt = { module = "dev.usbharu:emoji-kt", version = "2.0.1" } logback-ecs-encoder = { module = "co.elastic.logging:logback-ecs-encoder", version = "1.6.0" } From 8c3ff077d83d9d0ed49141567cede2042d924729 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 22:27:40 +0900 Subject: [PATCH 07/11] =?UTF-8?q?fix:=20=E3=82=BB=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=81=8C=E5=AD=98=E5=9C=A8=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E6=99=82=E3=81=AB=E4=BD=9C=E6=88=90=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/infrastructure/springframework/SPAInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt index b3a6eeb0..adb4b9d5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt @@ -29,7 +29,7 @@ class SPAInterceptor : HandlerInterceptor { return } - if (request.session.getAttribute("s") == "f") { + if (request.getSession(false).getAttribute("s") == "f") { return } From 5eb3bc3704d8c05df2ff8381414d852da193e019 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:00:59 +0900 Subject: [PATCH 08/11] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E5=8F=96=E3=82=8A=E6=B6=88?= =?UTF-8?q?=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/reaction/RemoveReaction.kt | 7 +++ .../UserRemoveReactionApplicationService.kt | 50 +++++++++++++++++++ .../core/domain/model/reaction/Reaction.kt | 4 ++ .../model/reaction/ReactionRepository.kt | 7 +++ .../ExposedPostRepository.kt | 2 +- .../ExposedReactionRepository.kt | 15 ++++++ .../interfaces/web/posts/PostsController.kt | 18 ++++++- .../resources/db/migration/V1__Init_DB.sql | 3 +- .../resources/templates/fragments-post.html | 29 ++++++++--- 9 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/RemoveReaction.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/RemoveReaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/RemoveReaction.kt new file mode 100644 index 00000000..6a7dd7ff --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/RemoveReaction.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.application.reaction + +data class RemoveReaction( + val postId: Long, + val customEmojiId: Long?, + val unicodeEmoji: String +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt new file mode 100644 index 00000000..b53f21f8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt @@ -0,0 +1,50 @@ +package dev.usbharu.hideout.core.application.reaction + +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService +import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository +import dev.usbharu.hideout.core.domain.model.support.principal.LocalUser +import dev.usbharu.hideout.core.domain.service.emoji.UnicodeEmojiService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserRemoveReactionApplicationService( + transaction: Transaction, + private val customEmojiRepository: CustomEmojiRepository, + private val reactionRepository: ReactionRepository, + private val unicodeEmojiService: UnicodeEmojiService +) : + LocalUserAbstractApplicationService( + transaction, logger + ) { + override suspend fun internalExecute(command: RemoveReaction, principal: LocalUser) { + val postId = PostId(command.postId) + + val customEmoji = command.customEmojiId?.let { customEmojiRepository.findById(it) } + + val unicodeEmoji = if (unicodeEmojiService.isUnicodeEmoji(command.unicodeEmoji)) { + command.unicodeEmoji + } else { + "❤" + } + val reaction = + reactionRepository.findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji( + postId, + principal.actorId, + customEmoji?.id, + unicodeEmoji + ) + ?: throw IllegalArgumentException("Reaction $postId ${principal.actorId} ${customEmoji?.id} $unicodeEmoji not found.") + + reaction.delete() + + reactionRepository.delete(reaction) + } + + companion object { + private val logger = LoggerFactory.getLogger(UserRemoveReactionApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt index ea3eac76..93b43000 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt @@ -32,6 +32,10 @@ class Reaction( return id.hashCode() } + fun delete() { + addDomainEvent(ReactionEventFactory(this).createEvent(ReactionEvent.DELETE)) + } + companion object { fun create( id: ReactionId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index f2ed4779..9aa1f6aa 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -15,5 +15,12 @@ interface ReactionRepository { unicodeEmoji: String ): Boolean + suspend fun findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji( + postId: PostId, + actorId: ActorId, + customEmojiId: CustomEmojiId?, + unicodeEmoji: String + ): Reaction? + suspend fun delete(reaction: Reaction) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index 9408d01a..37a476b1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -168,7 +168,7 @@ class ExposedPostRepository( Posts.id eq id.id } .let(postQueryMapper::map) - .first() + .firstOrNull() } override suspend fun findAllById(ids: List): List { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt index f3ac7b96..f3fc6886 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt @@ -82,6 +82,21 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu } } + override suspend fun findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji( + postId: PostId, + actorId: ActorId, + customEmojiId: CustomEmojiId?, + unicodeEmoji: String + ): Reaction? { + return query { + + Reactions.selectAll().where { + Reactions.postId.eq(postId.id).and(Reactions.actorId eq actorId.id) + .and((Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji))) + }.limit(1).singleOrNull()?.toReaction() + } + } + companion object { private val logger = LoggerFactory.getLogger(ExposedReactionRepository::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt index 0f6558c6..26e31f3c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt @@ -5,7 +5,9 @@ import dev.usbharu.hideout.core.application.instance.GetLocalInstanceApplication import dev.usbharu.hideout.core.application.post.GetPostDetail import dev.usbharu.hideout.core.application.post.GetPostDetailApplicationService import dev.usbharu.hideout.core.application.reaction.CreateReaction +import dev.usbharu.hideout.core.application.reaction.RemoveReaction import dev.usbharu.hideout.core.application.reaction.UserCreateReactionApplicationService +import dev.usbharu.hideout.core.application.reaction.UserRemoveReactionApplicationService import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder import org.springframework.security.access.AccessDeniedException import org.springframework.stereotype.Controller @@ -19,7 +21,8 @@ class PostsController( private val getPostDetailApplicationService: GetPostDetailApplicationService, private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder, private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService, - private val userCreateReactionApplicationService: UserCreateReactionApplicationService + private val userCreateReactionApplicationService: UserCreateReactionApplicationService, + private val userRemoveReactionApplicationService: UserRemoveReactionApplicationService ) { @GetMapping("/users/{name}/posts/{id}") suspend fun postById(@PathVariable id: Long, model: Model): String { @@ -48,4 +51,17 @@ class PostsController( ) return "redirect:/users/$name/posts/$id" } + + @PostMapping("/users/{name}/posts/{id}/unfavourite") + suspend fun unfavourite(@PathVariable id: Long, @PathVariable name: String): String { + val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal() + userRemoveReactionApplicationService.execute( + RemoveReaction( + id, + null, + "❤" + ), principal + ) + return "redirect:/users/$name/posts/$id" + } } diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 44bfbcf5..f4c62702 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -329,5 +329,6 @@ create table if not exists reactions unicode_emoji varchar(100) not null, created_at timestamp not null, constraint fk_reactions_post_id__id foreign key (post_id) references posts (id) on delete cascade on update cascade, - constraint fk_reactions_actor_id__id foreign key (actor_id) references actors (id) on delete cascade on update cascade + constraint fk_reactions_actor_id__id foreign key (actor_id) references actors (id) on delete cascade on update cascade, + unique (post_id, actor_id, created_at, unicode_emoji) ); \ No newline at end of file diff --git a/hideout-core/src/main/resources/templates/fragments-post.html b/hideout-core/src/main/resources/templates/fragments-post.html index 671cec83..d16f4861 100644 --- a/hideout-core/src/main/resources/templates/fragments-post.html +++ b/hideout-core/src/main/resources/templates/fragments-post.html @@ -44,14 +44,27 @@
-
- Reply - - - - -
+ +
+ Reply + + + + +
+
+ +
+ Reply + + + + +
+
+
From 7665286270fbc75264fb507b21ffdc477fa77353 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:01:19 +0900 Subject: [PATCH 09/11] =?UTF-8?q?fix:=20=E3=82=BB=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=81=8C=E5=AD=98=E5=9C=A8=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E6=99=82=E3=81=AB=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=8C?= =?UTF-8?q?=E5=87=BA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/infrastructure/springframework/SPAInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt index adb4b9d5..b3d9a394 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SPAInterceptor.kt @@ -29,7 +29,7 @@ class SPAInterceptor : HandlerInterceptor { return } - if (request.getSession(false).getAttribute("s") == "f") { + if (request.getSession(false)?.getAttribute("s") == "f") { return } From 021f6b0d45cdc405577e1934d03e97e0a667de6a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:32:01 +0900 Subject: [PATCH 10/11] =?UTF-8?q?feat:=20mastodon=20=E4=BA=92=E6=8F=9BAPI?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interfaces/api/SpringStatusApi.kt | 22 +++++++++- .../src/main/resources/openapi/mastodon.yaml | 42 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt index 5e045a59..10e54af0 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt @@ -18,6 +18,10 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.post.RegisterLocalPost import dev.usbharu.hideout.core.application.post.RegisterLocalPostApplicationService +import dev.usbharu.hideout.core.application.reaction.CreateReaction +import dev.usbharu.hideout.core.application.reaction.RemoveReaction +import dev.usbharu.hideout.core.application.reaction.UserCreateReactionApplicationService +import dev.usbharu.hideout.core.application.reaction.UserRemoveReactionApplicationService import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SpringSecurityOauth2PrincipalContextHolder import dev.usbharu.hideout.mastodon.application.status.GetStatus @@ -32,7 +36,9 @@ import org.springframework.stereotype.Controller class SpringStatusApi( private val registerLocalPostApplicationService: RegisterLocalPostApplicationService, private val getStatusApplicationService: GetStatusApplicationService, - private val principalContextHolder: SpringSecurityOauth2PrincipalContextHolder + private val principalContextHolder: SpringSecurityOauth2PrincipalContextHolder, + private val userCreateReactionApplicationService: UserCreateReactionApplicationService, + private val userRemoveReactionApplicationService: UserRemoveReactionApplicationService ) : StatusApi { override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity = super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji) @@ -49,6 +55,20 @@ class SpringStatusApi( ) } + override suspend fun apiV1StatusesIdFavouritePost(id: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() + + userCreateReactionApplicationService.execute(CreateReaction(postId = id.toLong(), null, "❤"), principal) + return ResponseEntity.ok(getStatusApplicationService.execute(GetStatus(id), principal)) + } + + override suspend fun apiV1StatusesIdUnfavouritePost(id: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() + + userRemoveReactionApplicationService.execute(RemoveReaction(postId = id.toLong(), null, "❤"), principal) + return ResponseEntity.ok(getStatusApplicationService.execute(GetStatus(id), principal)) + } + override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity { val principal = principalContextHolder.getPrincipal() val execute = registerLocalPostApplicationService.execute( diff --git a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml index bc5195d5..76d8f5aa 100644 --- a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml +++ b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml @@ -177,6 +177,48 @@ paths: schema: $ref: "#/components/schemas/Status" + /api/v1/statuses/{id}/favourite: + post: + tags: + - status + security: + - OAuth2: + - "write:favourites" + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + 200: + description: 成功 + content: + application/json: + schema: + $ref: "#/components/schemas/Status" + + /api/v1/statuses/{id}/unfavourite: + post: + tags: + - status + security: + - OAuth2: + - "write:favourites" + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + 200: + description: 成功 + content: + application/json: + schema: + $ref: "#/components/schemas/Status" + /api/v1/statuses/{id}/emoji_reactions/{emoji}: put: tags: From 587e87b35363816e142ff0e7758679abea1ca131 Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 9 Sep 2024 05:28:32 +0000 Subject: [PATCH 11/11] style: fix lint (CI) --- .../UserCreateReactionApplicationService.kt | 6 +++--- .../UserRemoveReactionApplicationService.kt | 5 +++-- .../domain/event/reaction/ReactionEvent.kt | 2 +- .../core/domain/model/emoji/CustomEmoji.kt | 6 +++--- .../core/domain/model/reaction/Reaction.kt | 10 +++++++--- .../core/domain/model/reaction/ReactionId.kt | 2 +- .../model/reaction/ReactionRepository.kt | 2 +- .../model/timelineobject/TimelineObject.kt | 12 ++++++------ .../service/emoji/UnicodeEmojiService.kt | 2 +- .../emojikt/EmojiKtUnicodeEmojiService.kt | 2 +- .../ExposedReactionsQueryService.kt | 3 +-- .../ExposedUserTimelineQueryService.kt | 17 ++++++++++------- .../ExposedReactionRepository.kt | 19 +++++++++++-------- .../timeline/AbstractTimelineStore.kt | 6 +++--- .../interfaces/web/posts/PostsController.kt | 6 ++++-- .../query/reactions/ReactionsQueryService.kt | 2 +- 16 files changed, 57 insertions(+), 45 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt index 1ca68aef..f0a7f2dd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt @@ -29,10 +29,10 @@ class UserCreateReactionApplicationService( private val unicodeEmojiService: UnicodeEmojiService ) : LocalUserAbstractApplicationService( - transaction, logger + transaction, + logger ) { override suspend fun internalExecute(command: CreateReaction, principal: LocalUser) { - val postId = PostId(command.postId) val post = postRepository.findById(postId) ?: throw IllegalArgumentException("Post $postId not found.") if (postReadAccessControl.isAllow(post, principal).not()) { @@ -62,4 +62,4 @@ class UserCreateReactionApplicationService( companion object { private val logger = LoggerFactory.getLogger(UserCreateReactionApplicationService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt index b53f21f8..4335ee63 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt @@ -18,7 +18,8 @@ class UserRemoveReactionApplicationService( private val unicodeEmojiService: UnicodeEmojiService ) : LocalUserAbstractApplicationService( - transaction, logger + transaction, + logger ) { override suspend fun internalExecute(command: RemoveReaction, principal: LocalUser) { val postId = PostId(command.postId) @@ -47,4 +48,4 @@ class UserRemoveReactionApplicationService( companion object { private val logger = LoggerFactory.getLogger(UserRemoveReactionApplicationService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt index 7e89361e..6da7a58c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt @@ -19,4 +19,4 @@ class ReactionEventBody( enum class ReactionEvent(val eventName: String) { CREATE("ReactionCreate"), DELETE("ReactionDelete"), -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt index 5a64a948..b80d06cf 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt @@ -29,9 +29,9 @@ sealed class Emoji { abstract fun id(): String override fun toString(): String { return "Emoji(" + - "domain='$domain', " + - "name='$name'" + - ")" + "domain='$domain', " + + "name='$name'" + + ")" } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt index 93b43000..c85c061d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt @@ -18,7 +18,6 @@ class Reaction( val createdAt: Instant ) : DomainEventStorable() { - override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -46,8 +45,13 @@ class Reaction( createdAt: Instant ): Reaction { return Reaction( - id, postId, actorId, customEmojiId, unicodeEmoji, createdAt + id, + postId, + actorId, + customEmojiId, + unicodeEmoji, + createdAt ).apply { addDomainEvent(ReactionEventFactory(this).createEvent(ReactionEvent.CREATE)) } } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt index af369ff0..948fb1ea 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt @@ -1,4 +1,4 @@ package dev.usbharu.hideout.core.domain.model.reaction @JvmInline -value class ReactionId(val value: Long) \ No newline at end of file +value class ReactionId(val value: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index 9aa1f6aa..01e893a7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -23,4 +23,4 @@ interface ReactionRepository { ): Reaction? suspend fun delete(reaction: Reaction) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt index a6fbe671..f7f57f22 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt @@ -61,9 +61,9 @@ class TimelineObject( lastUpdatedAt = Instant.now() isPureRepost = post.repostId != null && - post.replyId == null && - post.text.isEmpty() && - post.overview?.overview.isNullOrEmpty() + post.replyId == null && + post.text.isEmpty() && + post.overview?.overview.isNullOrEmpty() warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) } } @@ -133,9 +133,9 @@ class TimelineObject( repostActorId = repost.actorId, visibility = post.visibility, isPureRepost = repost.mediaIds.isEmpty() && - repost.overview == null && - repost.content == PostContent.empty && - repost.replyId == null, + repost.overview == null && + repost.content == PostContent.empty && + repost.replyId == null, mediaIds = post.mediaIds, emojiIds = post.emojiIds, visibleActors = post.visibleActors.toList(), diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt index add7bdbe..35a44ca5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt @@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.service.emoji interface UnicodeEmojiService { fun isUnicodeEmoji(emoji: String): Boolean -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt index 3eef34aa..3e0fdc43 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt @@ -9,4 +9,4 @@ class EmojiKtUnicodeEmojiService : UnicodeEmojiService { override fun isUnicodeEmoji(emoji: String): Boolean { return Emojis.allEmojis.singleOrNull { it.char == emoji } != null } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt index b4d7705f..d22184e7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt @@ -37,7 +37,6 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() override suspend fun findAllByPostIdIn(postIds: List): List { return query { - val actorIdsQuery = ExposedrepositoryReactions.actorId.castTo(VarCharColumnType()).groupConcat(",", true) @@ -71,4 +70,4 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() companion object { private val logger = LoggerFactory.getLogger(ExposedReactionsQueryService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt index 6b4338fb..db3fb0f2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt @@ -42,10 +42,10 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi .select(Posts.columns) .where { Posts.visibility eq Visibility.PUBLIC.name or - (Posts.visibility eq Visibility.UNLISTED.name) or - (Posts.visibility eq Visibility.DIRECT.name and (PostsVisibleActors.actorId eq principal.actorId.id)) or - (Posts.visibility eq Visibility.FOLLOWERS.name and (Relationships.blocking eq false and (relationshipsAlias[Relationships.following] eq true))) or - (Posts.actorId eq principal.actorId.id) + (Posts.visibility eq Visibility.UNLISTED.name) or + (Posts.visibility eq Visibility.DIRECT.name and (PostsVisibleActors.actorId eq principal.actorId.id)) or + (Posts.visibility eq Visibility.FOLLOWERS.name and (Relationships.blocking eq false and (relationshipsAlias[Relationships.following] eq true))) or + (Posts.actorId eq principal.actorId.id) } .alias("authorized_table") } @@ -61,10 +61,12 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi .leftJoin(iconMedia, { Actors.icon }, { iconMedia[Media.id] }) .leftJoin(PostsMedia, { authorizedQuery[Posts.id] }, { PostsMedia.postId }) .leftJoin(Media, { PostsMedia.mediaId }, { Media.id }) - .leftJoin(Reactions, + .leftJoin( + Reactions, { authorizedQuery[Posts.id] }, { Reactions.postId }, - { Reactions.id isDistinctFrom principal.actorId.id }) + { Reactions.id isDistinctFrom principal.actorId.id } + ) .selectAll() .where { authorizedQuery[Posts.id] inList idList.map { it.id } } .groupBy { it[authorizedQuery[Posts.id]] } @@ -73,7 +75,8 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi toPostDetail(it.first(), authorizedQuery, iconMedia).copy( mediaDetailList = it.mapNotNull { resultRow -> resultRow.toMediaOrNull()?.let { it1 -> MediaDetail.of(it1) } - }, favourited = it.any { it.getOrNull(Reactions.actorId) != null } + }, + favourited = it.any { it.getOrNull(Reactions.actorId) != null } ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt index f3fc6886..d44bca1f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt @@ -17,13 +17,14 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class ExposedReactionRepository(override val domainEventPublisher: DomainEventPublisher) : ReactionRepository, - AbstractRepository(), DomainEventPublishableRepository { +class ExposedReactionRepository(override val domainEventPublisher: DomainEventPublisher) : + ReactionRepository, + AbstractRepository(), + DomainEventPublishableRepository { override val logger: Logger get() = Companion.logger - override suspend fun save(reaction: Reaction): Reaction { return query { Reactions.upsert { @@ -66,7 +67,9 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu return query { Reactions.selectAll().where { Reactions.postId.eq(postId.id).and(Reactions.actorId eq actorId.id) - .and((Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji))) + .and( + (Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji)) + ) }.empty().not() } } @@ -89,10 +92,11 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu unicodeEmoji: String ): Reaction? { return query { - Reactions.selectAll().where { Reactions.postId.eq(postId.id).and(Reactions.actorId eq actorId.id) - .and((Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji))) + .and( + (Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji)) + ) }.limit(1).singleOrNull()?.toReaction() } } @@ -111,7 +115,6 @@ fun ResultRow.toReaction(): Reaction { UnicodeEmoji(this[Reactions.unicodeEmoji]), this[Reactions.createdAt] ) - } object Reactions : Table("reactions") { @@ -122,4 +125,4 @@ object Reactions : Table("reactions") { val unicodeEmoji = varchar("unicode_emoji", 100) val createdAt = timestamp("created_at") override val primaryKey: PrimaryKey = PrimaryKey(id) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt index 9ddcc131..b22d0121 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt @@ -246,8 +246,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe val actors = getActors( timelineObjectList.map { it.postActorId } + - timelineObjectList.mapNotNull { it.repostActorId } + - timelineObjectList.mapNotNull { it.replyActorId } + timelineObjectList.mapNotNull { it.repostActorId } + + timelineObjectList.mapNotNull { it.replyActorId } ) val postMap = posts.associate { post -> @@ -256,7 +256,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe val mediaMap = getMedias( posts.flatMap { it.mediaIds } + - actors.mapNotNull { it.value.icon } + actors.mapNotNull { it.value.icon } ) val reactions = getReactions(posts.map { it.id }) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt index 26e31f3c..f1005327 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt @@ -47,7 +47,8 @@ class PostsController( id, null, "❤" - ), principal + ), + principal ) return "redirect:/users/$name/posts/$id" } @@ -60,7 +61,8 @@ class PostsController( id, null, "❤" - ), principal + ), + principal ) return "redirect:/users/$name/posts/$id" } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt index 42164bc0..e410c4ae 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt @@ -6,4 +6,4 @@ import dev.usbharu.hideout.core.domain.model.post.PostId interface ReactionsQueryService { suspend fun findAllByPostId(postId: PostId): List suspend fun findAllByPostIdIn(postIds: List): List -} \ No newline at end of file +}