mirror of https://github.com/usbharu/Hideout.git
feat: リアクションを取り消せるように
This commit is contained in:
parent
8c3ff077d8
commit
5eb3bc3704
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.usbharu.hideout.core.application.reaction
|
||||||
|
|
||||||
|
data class RemoveReaction(
|
||||||
|
val postId: Long,
|
||||||
|
val customEmojiId: Long?,
|
||||||
|
val unicodeEmoji: String
|
||||||
|
)
|
|
@ -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<RemoveReaction, Unit>(
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,10 @@ class Reaction(
|
||||||
return id.hashCode()
|
return id.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun delete() {
|
||||||
|
addDomainEvent(ReactionEventFactory(this).createEvent(ReactionEvent.DELETE))
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(
|
fun create(
|
||||||
id: ReactionId,
|
id: ReactionId,
|
||||||
|
|
|
@ -15,5 +15,12 @@ interface ReactionRepository {
|
||||||
unicodeEmoji: String
|
unicodeEmoji: String
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
|
suspend fun findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji(
|
||||||
|
postId: PostId,
|
||||||
|
actorId: ActorId,
|
||||||
|
customEmojiId: CustomEmojiId?,
|
||||||
|
unicodeEmoji: String
|
||||||
|
): Reaction?
|
||||||
|
|
||||||
suspend fun delete(reaction: Reaction)
|
suspend fun delete(reaction: Reaction)
|
||||||
}
|
}
|
|
@ -168,7 +168,7 @@ class ExposedPostRepository(
|
||||||
Posts.id eq id.id
|
Posts.id eq id.id
|
||||||
}
|
}
|
||||||
.let(postQueryMapper::map)
|
.let(postQueryMapper::map)
|
||||||
.first()
|
.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findAllById(ids: List<PostId>): List<Post> {
|
override suspend fun findAllById(ids: List<PostId>): List<Post> {
|
||||||
|
|
|
@ -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 {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(ExposedReactionRepository::class.java)
|
private val logger = LoggerFactory.getLogger(ExposedReactionRepository::class.java)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.GetPostDetail
|
||||||
import dev.usbharu.hideout.core.application.post.GetPostDetailApplicationService
|
import dev.usbharu.hideout.core.application.post.GetPostDetailApplicationService
|
||||||
import dev.usbharu.hideout.core.application.reaction.CreateReaction
|
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.UserCreateReactionApplicationService
|
||||||
|
import dev.usbharu.hideout.core.application.reaction.UserRemoveReactionApplicationService
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder
|
import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder
|
||||||
import org.springframework.security.access.AccessDeniedException
|
import org.springframework.security.access.AccessDeniedException
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
|
@ -19,7 +21,8 @@ class PostsController(
|
||||||
private val getPostDetailApplicationService: GetPostDetailApplicationService,
|
private val getPostDetailApplicationService: GetPostDetailApplicationService,
|
||||||
private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder,
|
private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder,
|
||||||
private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService,
|
private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService,
|
||||||
private val userCreateReactionApplicationService: UserCreateReactionApplicationService
|
private val userCreateReactionApplicationService: UserCreateReactionApplicationService,
|
||||||
|
private val userRemoveReactionApplicationService: UserRemoveReactionApplicationService
|
||||||
) {
|
) {
|
||||||
@GetMapping("/users/{name}/posts/{id}")
|
@GetMapping("/users/{name}/posts/{id}")
|
||||||
suspend fun postById(@PathVariable id: Long, model: Model): String {
|
suspend fun postById(@PathVariable id: Long, model: Model): String {
|
||||||
|
@ -48,4 +51,17 @@ class PostsController(
|
||||||
)
|
)
|
||||||
return "redirect:/users/$name/posts/$id"
|
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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -329,5 +329,6 @@ create table if not exists reactions
|
||||||
unicode_emoji varchar(100) not null,
|
unicode_emoji varchar(100) not null,
|
||||||
created_at timestamp 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_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)
|
||||||
);
|
);
|
|
@ -44,14 +44,27 @@
|
||||||
|
|
||||||
<div class="post-controller" th:fragment="single-post-controller(post)">
|
<div class="post-controller" th:fragment="single-post-controller(post)">
|
||||||
<!--/*@thymesVar id="post" type="dev.usbharu.hideout.core.application.post.PostDetail"*/-->
|
<!--/*@thymesVar id="post" type="dev.usbharu.hideout.core.application.post.PostDetail"*/-->
|
||||||
<form method="post" th:action="@{/users/a/posts/{id}/favourite(id=${post.id})}">
|
<th:block th:if="${post.favourited}">
|
||||||
<a th:href="${'/publish?reply_to=' + post.id}">Reply</a>
|
<form method="post" th:action="@{/users/a/posts/{id}/unfavourite(id=${post.id})}">
|
||||||
<input type="submit" value="❤">
|
<a th:href="${'/publish?reply_to=' + post.id}">Reply</a>
|
||||||
<a th:href="${post.apId}">
|
<input type="submit" value="[❤]">
|
||||||
<time th:datetime="${post.createdAt}"
|
<a th:href="${post.apId}">
|
||||||
th:text="${#temporals.format(post.createdAt, 'yyyy-MM-dd HH:mm')}"></time>
|
<time th:datetime="${post.createdAt}"
|
||||||
</a>
|
th:text="${#temporals.format(post.createdAt, 'yyyy-MM-dd HH:mm')}"></time>
|
||||||
</form>
|
</a>
|
||||||
|
</form>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:unless="${post.favourited}">
|
||||||
|
<form method="post" th:action="@{/users/a/posts/{id}/favourite(id=${post.id})}">
|
||||||
|
<a th:href="${'/publish?reply_to=' + post.id}">Reply</a>
|
||||||
|
<input type="submit" value="❤">
|
||||||
|
<a th:href="${post.apId}">
|
||||||
|
<time th:datetime="${post.createdAt}"
|
||||||
|
th:text="${#temporals.format(post.createdAt, 'yyyy-MM-dd HH:mm')}"></time>
|
||||||
|
</a>
|
||||||
|
</form>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue