feat: タイムラインを読めるように

This commit is contained in:
usbharu 2024-08-15 18:22:56 +09:00
parent 88a61ba97f
commit 711084e366
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
7 changed files with 138 additions and 10 deletions

View File

@ -0,0 +1,10 @@
package dev.usbharu.hideout.core.application.domainevent.subscribers
class RegisterLocalUserSetHomeTimelineSubscriber(private val domainEventSubscriber: DomainEventSubscriber) :
Subscriber {
init {
domainEventSubscriber.subscribe<>()
}
}
//todo userdetailにdomain event付けて createのイベントで反応させる タイムラインを新しく一つ作って userdetailのhometimelineに紐づけて自分自身をtimleine relationshipに入れる

View File

@ -2,17 +2,18 @@ package dev.usbharu.hideout.core.application.domainevent.subscribers
import dev.usbharu.hideout.core.domain.event.post.PostEvent import dev.usbharu.hideout.core.domain.event.post.PostEvent
import dev.usbharu.hideout.core.domain.event.post.PostEventBody import dev.usbharu.hideout.core.domain.event.post.PostEventBody
import dev.usbharu.hideout.core.external.timeline.TimelineStore
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class TimelinePostCreateSubscriber(domainEventSubscriber: DomainEventSubscriber) : Subscriber { class TimelinePostCreateSubscriber(
private val timelineStore: TimelineStore,
domainEventSubscriber: DomainEventSubscriber
) : Subscriber {
init { init {
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName) { domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName) {
val post = it.body.getPost() timelineStore.addPost(it.body.getPost())
val actor = it.body.getActor()
logger.info("New Post! : {}", post)
} }
} }

View File

@ -1,11 +1,11 @@
package dev.usbharu.hideout.core.application.timeline package dev.usbharu.hideout.core.application.timeline
import dev.usbharu.hideout.core.application.post.PostDetail
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.Principal
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
@ -19,11 +19,11 @@ class ReadTimelineApplicationService(
private val timelineRepository: TimelineRepository, private val timelineRepository: TimelineRepository,
transaction: Transaction transaction: Transaction
) : ) :
AbstractApplicationService<ReadTimeline, PaginationList<TimelineObjectDetail, PostId>>(transaction, logger) { AbstractApplicationService<ReadTimeline, PaginationList<PostDetail, PostId>>(transaction, logger) {
override suspend fun internalExecute( override suspend fun internalExecute(
command: ReadTimeline, command: ReadTimeline,
principal: Principal principal: Principal
): PaginationList<TimelineObjectDetail, PostId> { ): PaginationList<PostDetail, PostId> {
val findById = timelineRepository.findById(TimelineId(command.timelineId)) val findById = timelineRepository.findById(TimelineId(command.timelineId))
?: throw IllegalArgumentException("Timeline ${command.timelineId} not found.") ?: throw IllegalArgumentException("Timeline ${command.timelineId} not found.")
@ -33,12 +33,48 @@ class ReadTimelineApplicationService(
command.remoteOnly command.remoteOnly
) )
return timelineStore.readTimeline( val timeline = timelineStore.readTimeline(
findById, findById,
readTimelineOption, readTimelineOption,
command.page, command.page,
principal, principal,
) )
val postDetailList = timeline.map {
val reply = if (it.replyPost != null) {
PostDetail.of(
it.replyPost,
it.replyPostActor!!,
it.replyPostActorIconMedia,
it.replyPostMedias.orEmpty()
)
} else {
null
}
val repost = if (it.repostPost != null) {
PostDetail.of(
it.repostPost,
it.repostPostActor!!,
it.repostPostActorIconMedia,
it.repostPostMedias.orEmpty()
)
} else {
null
}
PostDetail.of(
it.post,
it.postActor,
it.postActorIconMedia,
it.postMedias,
reply,
repost
)
}
return PaginationList(postDetailList, timeline.next, timeline.prev)
} }
companion object { companion object {

View File

@ -52,7 +52,7 @@ import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
@Configuration @Configuration
@EnableWebSecurity(debug = true) @EnableWebSecurity(debug = false)
class SecurityConfig { class SecurityConfig {
@Bean @Bean
fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()

View File

@ -0,0 +1,48 @@
package dev.usbharu.hideout.core.interfaces.web.timeline
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.application.timeline.ReadTimeline
import dev.usbharu.hideout.core.application.timeline.ReadTimelineApplicationService
import dev.usbharu.hideout.core.domain.model.support.page.Page
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
@Controller
class TimelineController(
private val readTimelineApplicationService: ReadTimelineApplicationService,
private val userDetailRepository: UserDetailRepository,
private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder,
private val transaction: Transaction
) {
@GetMapping("/home")
suspend fun homeTimeline(model: Model, @RequestParam sinceId: String?, @RequestParam maxId: String?): String {
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
val userDetail = transaction.transaction {
userDetailRepository.findByActorId(principal.actorId.id)
?: throw InternalServerException("UserDetail not found.")
}
val homeTimelineId = userDetail.homeTimelineId!!
val execute = readTimelineApplicationService.execute(
ReadTimeline(
timelineId = homeTimelineId.value,
mediaOnly = false,
localOnly = false,
remoteOnly = false,
page = Page.of(
maxId = maxId?.toLongOrNull(),
sinceId = sinceId?.toLongOrNull()
)
), principal
)
model.addAttribute("timeline", execute)
return "homeTimeline"
}
}

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<th:block th:fragment="simple-timline(timelineObject,href)">
<!--/*@thymesVar id="timelineObject" type="dev.usbharu.hideout.core.domain.model.support.page.PaginationList<dev.usbharu.hideout.core.application.post.PostDetail,dev.usbharu.hideout.core.domain.model.post.PostId>"*/-->
<div th:if="${timelineObject.prev == null}">
<a th:href="${href + '/?maxId=' + timelineObject.prev}"></a>
</div>
<div th:each="postDetail : ${timelineObject}">
<th:block th:replace="fragments-post :: single-simple-post(${postDetail})"></th:block>
</div>
<div th:if="${timelineObject.next == null}">
<a th:href="${href + '/?sinceId=' + timelineObject.next}"></a>
</div>
</th:block>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<noscript>
<div th:replace="fragments-timeline :: simple-timline(${timeline},'/home')"></div>
</noscript>
</body>
</html>