mirror of https://github.com/usbharu/Hideout.git
feat: カスタム絵文字のリアクションの対応
This commit is contained in:
parent
0607fe2bc5
commit
32e60dc6f8
|
@ -12,9 +12,9 @@ data class CustomEmoji(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val domain: String,
|
override val domain: String,
|
||||||
val instanceId: Long,
|
val instanceId: Long?,
|
||||||
val url: String,
|
val url: String,
|
||||||
val category: String,
|
val category: String?,
|
||||||
val createdAt: Instant
|
val createdAt: Instant
|
||||||
) : Emoji() {
|
) : Emoji() {
|
||||||
override fun id(): String {
|
override fun id(): String {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.reaction
|
package dev.usbharu.hideout.core.domain.model.reaction
|
||||||
|
|
||||||
data class Reaction(val id: Long, val emojiId: Long, val postId: Long, val actorId: Long)
|
import dev.usbharu.hideout.core.domain.model.emoji.Emoji
|
||||||
|
|
||||||
|
data class Reaction(val id: Long, val emoji: Emoji, val postId: Long, val actorId: Long)
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||||
|
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.javatime.CurrentTimestamp
|
||||||
|
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
class CustomEmojiRepositoryImpl : CustomEmojiRepository, AbstractRepository() {
|
||||||
|
override suspend fun save(customEmoji: CustomEmoji): CustomEmoji = query {
|
||||||
|
val singleOrNull = CustomEmojis.select { CustomEmojis.id eq customEmoji.id }.forUpdate().singleOrNull()
|
||||||
|
if (singleOrNull == null) {
|
||||||
|
CustomEmojis.insert {
|
||||||
|
it[CustomEmojis.id] = customEmoji.id
|
||||||
|
it[CustomEmojis.name] = customEmoji.name
|
||||||
|
it[CustomEmojis.domain] = customEmoji.domain
|
||||||
|
it[CustomEmojis.instanceId] = customEmoji.instanceId
|
||||||
|
it[CustomEmojis.url] = customEmoji.url
|
||||||
|
it[CustomEmojis.category] = customEmoji.category
|
||||||
|
it[CustomEmojis.createdAt] = customEmoji.createdAt
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CustomEmojis.update({ CustomEmojis.id eq customEmoji.id }) {
|
||||||
|
it[CustomEmojis.name] = customEmoji.name
|
||||||
|
it[CustomEmojis.domain] = customEmoji.domain
|
||||||
|
it[CustomEmojis.instanceId] = customEmoji.instanceId
|
||||||
|
it[CustomEmojis.url] = customEmoji.url
|
||||||
|
it[CustomEmojis.category] = customEmoji.category
|
||||||
|
it[CustomEmojis.createdAt] = customEmoji.createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@query customEmoji
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findById(id: Long): CustomEmoji? = query {
|
||||||
|
return@query CustomEmojis.select { CustomEmojis.id eq id }.singleOrNull()?.toCustomEmoji()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(customEmoji: CustomEmoji): Unit = query {
|
||||||
|
CustomEmojis.deleteWhere { CustomEmojis.id eq customEmoji.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val logger: Logger
|
||||||
|
get() = Companion.logger
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(CustomEmojiRepositoryImpl::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji(
|
||||||
|
this[CustomEmojis.id],
|
||||||
|
this[CustomEmojis.name],
|
||||||
|
this[CustomEmojis.domain],
|
||||||
|
this[CustomEmojis.instanceId],
|
||||||
|
this[CustomEmojis.url],
|
||||||
|
this[CustomEmojis.category],
|
||||||
|
this[CustomEmojis.createdAt]
|
||||||
|
)
|
||||||
|
|
||||||
|
object CustomEmojis : Table("emojis") {
|
||||||
|
val id = long("id")
|
||||||
|
val name = varchar("name", 1000)
|
||||||
|
val domain = varchar("domain", 1000)
|
||||||
|
val instanceId = long("instance_id").references(Instance.id).nullable()
|
||||||
|
val url = varchar("url", 255).uniqueIndex()
|
||||||
|
val category = varchar("category", 255).nullable()
|
||||||
|
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp())
|
||||||
|
|
||||||
|
override val primaryKey: PrimaryKey = PrimaryKey(id)
|
||||||
|
|
||||||
|
init {
|
||||||
|
uniqueIndex(name, instanceId)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||||
|
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||||
|
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
||||||
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
|
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
|
||||||
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
|
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||||
|
@ -23,13 +25,25 @@ class ReactionRepositoryImpl(
|
||||||
if (Reactions.select { Reactions.id eq reaction.id }.forUpdate().empty()) {
|
if (Reactions.select { Reactions.id eq reaction.id }.forUpdate().empty()) {
|
||||||
Reactions.insert {
|
Reactions.insert {
|
||||||
it[id] = reaction.id
|
it[id] = reaction.id
|
||||||
it[emojiId] = reaction.emojiId
|
if (reaction.emoji is CustomEmoji) {
|
||||||
|
it[customEmojiId] = reaction.emoji.id
|
||||||
|
it[unicodeEmoji] = null
|
||||||
|
} else {
|
||||||
|
it[customEmojiId] = null
|
||||||
|
it[unicodeEmoji] = reaction.emoji.name
|
||||||
|
}
|
||||||
it[postId] = reaction.postId
|
it[postId] = reaction.postId
|
||||||
it[actorId] = reaction.actorId
|
it[actorId] = reaction.actorId
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Reactions.update({ Reactions.id eq reaction.id }) {
|
Reactions.update({ Reactions.id eq reaction.id }) {
|
||||||
it[emojiId] = reaction.emojiId
|
if (reaction.emoji is CustomEmoji) {
|
||||||
|
it[customEmojiId] = reaction.emoji.id
|
||||||
|
it[unicodeEmoji] = null
|
||||||
|
} else {
|
||||||
|
it[customEmojiId] = null
|
||||||
|
it[unicodeEmoji] = reaction.emoji.name
|
||||||
|
}
|
||||||
it[postId] = reaction.postId
|
it[postId] = reaction.postId
|
||||||
it[actorId] = reaction.actorId
|
it[actorId] = reaction.actorId
|
||||||
}
|
}
|
||||||
|
@ -38,9 +52,16 @@ class ReactionRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(reaction: Reaction): Reaction = query {
|
override suspend fun delete(reaction: Reaction): Reaction = query {
|
||||||
Reactions.deleteWhere {
|
if (reaction.emoji is CustomEmoji) {
|
||||||
id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId))
|
Reactions.deleteWhere {
|
||||||
.and(emojiId.eq(reaction.emojiId))
|
id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId))
|
||||||
|
.and(customEmojiId.eq(reaction.emoji.id))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Reactions.deleteWhere {
|
||||||
|
id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId))
|
||||||
|
.and(unicodeEmoji.eq(reaction.emoji.name))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return@query reaction
|
return@query reaction
|
||||||
}
|
}
|
||||||
|
@ -58,14 +79,14 @@ class ReactionRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByPostId(postId: Long): List<Reaction> = query {
|
override suspend fun findByPostId(postId: Long): List<Reaction> = query {
|
||||||
return@query Reactions.select { Reactions.postId eq postId }.map { it.toReaction() }
|
return@query Reactions.leftJoin(CustomEmojis).select { Reactions.postId eq postId }.map { it.toReaction() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? =
|
override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? =
|
||||||
query {
|
query {
|
||||||
return@query Reactions.select {
|
return@query Reactions.leftJoin(CustomEmojis).select {
|
||||||
Reactions.postId eq postId and (Reactions.actorId eq actorId).and(
|
Reactions.postId eq postId and (Reactions.actorId eq actorId).and(
|
||||||
Reactions.emojiId.eq(
|
Reactions.customEmojiId.eq(
|
||||||
emojiId
|
emojiId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -78,12 +99,13 @@ class ReactionRepositoryImpl(
|
||||||
Reactions.postId
|
Reactions.postId
|
||||||
.eq(postId)
|
.eq(postId)
|
||||||
.and(Reactions.actorId.eq(actorId))
|
.and(Reactions.actorId.eq(actorId))
|
||||||
.and(Reactions.emojiId.eq(emojiId))
|
.and(Reactions.customEmojiId.eq(emojiId))
|
||||||
}.empty().not()
|
}.empty().not()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List<Reaction> = query {
|
override suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List<Reaction> = query {
|
||||||
return@query Reactions.select { Reactions.postId eq postId and (Reactions.actorId eq actorId) }
|
return@query Reactions.leftJoin(CustomEmojis)
|
||||||
|
.select { Reactions.postId eq postId and (Reactions.actorId eq actorId) }
|
||||||
.map { it.toReaction() }
|
.map { it.toReaction() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,22 +115,39 @@ class ReactionRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ResultRow.toReaction(): Reaction {
|
fun ResultRow.toReaction(): Reaction {
|
||||||
|
val emoji = if (this[Reactions.customEmojiId] != null) {
|
||||||
|
CustomEmoji(
|
||||||
|
this[Reactions.customEmojiId]!!,
|
||||||
|
this[CustomEmojis.name],
|
||||||
|
this[CustomEmojis.domain],
|
||||||
|
this[CustomEmojis.instanceId],
|
||||||
|
this[CustomEmojis.url],
|
||||||
|
this[CustomEmojis.category],
|
||||||
|
this[CustomEmojis.createdAt]
|
||||||
|
)
|
||||||
|
} else if (this[Reactions.unicodeEmoji] != null) {
|
||||||
|
UnicodeEmoji(this[Reactions.unicodeEmoji]!!)
|
||||||
|
} else {
|
||||||
|
throw IllegalStateException("customEmojiId and unicodeEmoji is null.")
|
||||||
|
}
|
||||||
|
|
||||||
return Reaction(
|
return Reaction(
|
||||||
this[Reactions.id].value,
|
this[Reactions.id].value,
|
||||||
this[Reactions.emojiId],
|
emoji,
|
||||||
this[Reactions.postId],
|
this[Reactions.postId],
|
||||||
this[Reactions.actorId]
|
this[Reactions.actorId]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Reactions : LongIdTable("reactions") {
|
object Reactions : LongIdTable("reactions") {
|
||||||
val emojiId: Column<Long> = long("emoji_id")
|
val customEmojiId = long("custom_emoji_id").references(CustomEmojis.id).nullable()
|
||||||
|
val unicodeEmoji = varchar("unicode_emoji", 255).nullable()
|
||||||
val postId: Column<Long> =
|
val postId: Column<Long> =
|
||||||
long("post_id").references(Posts.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
long("post_id").references(Posts.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
||||||
val actorId: Column<Long> =
|
val actorId: Column<Long> =
|
||||||
long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
uniqueIndex(emojiId, postId, actorId)
|
uniqueIndex(customEmojiId, postId, actorId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,15 +132,19 @@ alter table posts_emojis
|
||||||
|
|
||||||
create table if not exists reactions
|
create table if not exists reactions
|
||||||
(
|
(
|
||||||
id bigserial primary key,
|
id bigserial primary key,
|
||||||
emoji_id bigint not null,
|
unicode_emoji varchar(255) null default null,
|
||||||
post_id bigint not null,
|
custom_emoji_id bigint null default null,
|
||||||
actor_id bigint not null
|
post_id bigint not null,
|
||||||
|
actor_id bigint not null
|
||||||
);
|
);
|
||||||
alter table reactions
|
alter table reactions
|
||||||
add constraint fk_reactions_post_id__id foreign key (post_id) references posts (id) on delete restrict on update restrict;
|
add constraint fk_reactions_post_id__id foreign key (post_id) references posts (id) on delete restrict on update restrict;
|
||||||
alter table reactions
|
alter table reactions
|
||||||
add constraint fk_reactions_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict;
|
add constraint fk_reactions_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict;
|
||||||
|
alter table reactions
|
||||||
|
add constraint fk_reactions_emoji_id__id foreign key (emoji_id) references emojis (id) on delete cascade on update cascade;
|
||||||
|
|
||||||
create table if not exists timelines
|
create table if not exists timelines
|
||||||
(
|
(
|
||||||
id bigint primary key,
|
id bigint primary key,
|
||||||
|
|
Loading…
Reference in New Issue