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()
|
||||
}
|
||||
|
||||
fun delete() {
|
||||
addDomainEvent(ReactionEventFactory(this).createEvent(ReactionEvent.DELETE))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
id: ReactionId,
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -168,7 +168,7 @@ class ExposedPostRepository(
|
|||
Posts.id eq id.id
|
||||
}
|
||||
.let(postQueryMapper::map)
|
||||
.first()
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
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 {
|
||||
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.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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
|
@ -44,6 +44,17 @@
|
|||
|
||||
<div class="post-controller" th:fragment="single-post-controller(post)">
|
||||
<!--/*@thymesVar id="post" type="dev.usbharu.hideout.core.application.post.PostDetail"*/-->
|
||||
<th:block th:if="${post.favourited}">
|
||||
<form method="post" th:action="@{/users/a/posts/{id}/unfavourite(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>
|
||||
<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="❤">
|
||||
|
@ -52,6 +63,8 @@
|
|||
th:text="${#temporals.format(post.createdAt, 'yyyy-MM-dd HH:mm')}"></time>
|
||||
</a>
|
||||
</form>
|
||||
</th:block>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue