mirror of https://github.com/usbharu/Hideout.git
feat: ページングが動くように
This commit is contained in:
parent
d53cdca9e5
commit
11e2eb1e10
|
@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
|
@ -61,6 +62,22 @@ class MongoInternalTimelineObjectRepository(
|
|||
springDataMongoTimelineObjectRepository.deleteByTimelineId(timelineId.value)
|
||||
}
|
||||
|
||||
override suspend fun findByTimelineIdAndPostIdGT(timelineId: TimelineId, postId: PostId): TimelineObject? {
|
||||
return springDataMongoTimelineObjectRepository.findFirstByTimelineIdAndPostIdGreaterThanOrderByIdAsc(
|
||||
timelineId.value,
|
||||
postId.id
|
||||
)
|
||||
?.toTimelineObject()
|
||||
}
|
||||
|
||||
override suspend fun findByTimelineIdAndPostIdLT(timelineId: TimelineId, postId: PostId): TimelineObject? {
|
||||
return springDataMongoTimelineObjectRepository.findFirstByTimelineIdAndPostIdLessThanOrderByIdDesc(
|
||||
timelineId.value,
|
||||
postId.id
|
||||
)
|
||||
?.toTimelineObject()
|
||||
}
|
||||
|
||||
override suspend fun findByTimelineId(
|
||||
timelineId: TimelineId,
|
||||
internalTimelineObjectOption: InternalTimelineObjectOption?,
|
||||
|
@ -70,12 +87,12 @@ class MongoInternalTimelineObjectRepository(
|
|||
|
||||
if (page?.minId != null) {
|
||||
query.with(Sort.by(Sort.Direction.ASC, "postCreatedAt"))
|
||||
page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) }
|
||||
page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) }
|
||||
page.minId?.let { query.addCriteria(Criteria.where("postId").gt(it)) }
|
||||
page.maxId?.let { query.addCriteria(Criteria.where("postId").lt(it)) }
|
||||
} else {
|
||||
query.with(Sort.by(Sort.Direction.DESC, "postCreatedAt"))
|
||||
page?.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) }
|
||||
page?.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) }
|
||||
page?.sinceId?.let { query.addCriteria(Criteria.where("postId").gt(it)) }
|
||||
page?.maxId?.let { query.addCriteria(Criteria.where("postId").lt(it)) }
|
||||
}
|
||||
|
||||
page?.limit?.let { query.limit(it) }
|
||||
|
@ -83,16 +100,23 @@ class MongoInternalTimelineObjectRepository(
|
|||
val timelineObjects =
|
||||
mongoTemplate.find(query, SpringDataMongoTimelineObject::class.java).map { it.toTimelineObject() }
|
||||
|
||||
val objectList = if (page?.minId != null) {
|
||||
timelineObjects.reversed()
|
||||
} else {
|
||||
timelineObjects
|
||||
}
|
||||
|
||||
return PaginationList(
|
||||
timelineObjects,
|
||||
timelineObjects.lastOrNull()?.postId,
|
||||
timelineObjects.firstOrNull()?.postId
|
||||
objectList,
|
||||
objectList.lastOrNull()?.postId,
|
||||
objectList.firstOrNull()?.postId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Document
|
||||
data class SpringDataMongoTimelineObject(
|
||||
@Id
|
||||
val id: Long,
|
||||
val userDetailId: Long,
|
||||
val timelineId: Long,
|
||||
|
@ -194,4 +218,14 @@ interface SpringDataMongoTimelineObjectRepository : CoroutineCrudRepository<Spri
|
|||
suspend fun deleteByTimelineId(timelineId: Long)
|
||||
|
||||
suspend fun findByTimelineId(timelineId: TimelineId): Flow<SpringDataMongoTimelineObject>
|
||||
|
||||
suspend fun findFirstByTimelineIdAndPostIdGreaterThanOrderByIdAsc(
|
||||
timelineId: Long,
|
||||
postId: Long
|
||||
): SpringDataMongoTimelineObject?
|
||||
|
||||
suspend fun findFirstByTimelineIdAndPostIdLessThanOrderByIdDesc(
|
||||
timelineId: Long,
|
||||
postId: Long
|
||||
): SpringDataMongoTimelineObject?
|
||||
}
|
||||
|
|
|
@ -205,6 +205,11 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
|||
removeTimelineObject(timeline.id)
|
||||
}
|
||||
|
||||
protected abstract suspend fun getNextPaging(
|
||||
timelineId: TimelineId,
|
||||
page: Page?
|
||||
): PaginationList<TimelineObjectDetail, PostId>
|
||||
|
||||
override suspend fun readTimeline(
|
||||
timeline: Timeline,
|
||||
option: ReadTimelineOption?,
|
||||
|
@ -212,6 +217,9 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
|||
principal: Principal
|
||||
): PaginationList<TimelineObjectDetail, PostId> {
|
||||
val timelineObjectList = getTimelineObject(timeline.id, option, page)
|
||||
if (timelineObjectList.isEmpty()) {
|
||||
return getNextPaging(timeline.id, page)
|
||||
}
|
||||
val lastUpdatedAt = timelineObjectList.minBy { it.lastUpdatedAt }.lastUpdatedAt
|
||||
|
||||
val newerFilters = getNewerFilters(timeline.userDetailId, lastUpdatedAt)
|
||||
|
|
|
@ -18,6 +18,7 @@ import dev.usbharu.hideout.core.domain.model.post.Visibility
|
|||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
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.timelineobjectdetail.TimelineObjectDetail
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
||||
|
@ -135,6 +136,30 @@ open class DefaultTimelineStore(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getNextPaging(
|
||||
timelineId: TimelineId,
|
||||
page: Page?
|
||||
): PaginationList<TimelineObjectDetail, PostId> {
|
||||
if (page?.maxId != null) {
|
||||
return PaginationList(
|
||||
emptyList(),
|
||||
null,
|
||||
internalTimelineObjectRepository.findByTimelineIdAndPostIdLT(timelineId, PostId(page.maxId!!))?.postId
|
||||
?: PostId(0)
|
||||
)
|
||||
} else if (page?.minId != null) {
|
||||
return PaginationList(
|
||||
emptyList(),
|
||||
internalTimelineObjectRepository.findByTimelineIdAndPostIdGT(timelineId, PostId(page.minId!!))?.postId
|
||||
?: PostId(Long.MAX_VALUE),
|
||||
null
|
||||
)
|
||||
}
|
||||
return PaginationList(emptyList(), page?.maxId?.let { PostId(it) }, page?.minId?.let { PostId(it) })
|
||||
|
||||
|
||||
}
|
||||
|
||||
override suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor> {
|
||||
return actorRepository.findAllById(actorIds).associateBy { it.id }
|
||||
}
|
||||
|
|
|
@ -19,6 +19,17 @@ interface InternalTimelineObjectRepository {
|
|||
suspend fun deleteByTimelineIdAndActorId(timelineId: TimelineId, actorId: ActorId)
|
||||
|
||||
suspend fun deleteByTimelineId(timelineId: TimelineId)
|
||||
|
||||
/**
|
||||
* 指定したTimelineIdより大きく、近いものを返す
|
||||
*/
|
||||
suspend fun findByTimelineIdAndPostIdGT(timelineId: TimelineId, postId: PostId): TimelineObject?
|
||||
|
||||
/**
|
||||
* 指定したTimelineIdより小さく、近いものを返す
|
||||
*/
|
||||
suspend fun findByTimelineIdAndPostIdLT(timelineId: TimelineId, postId: PostId): TimelineObject?
|
||||
|
||||
suspend fun findByTimelineId(
|
||||
timelineId: TimelineId,
|
||||
internalTimelineObjectOption: InternalTimelineObjectOption? = null,
|
||||
|
|
|
@ -20,7 +20,12 @@ class TimelineController(
|
|||
private val transaction: Transaction
|
||||
) {
|
||||
@GetMapping("/home")
|
||||
suspend fun homeTimeline(model: Model, @RequestParam sinceId: String?, @RequestParam maxId: String?): String {
|
||||
suspend fun homeTimeline(
|
||||
model: Model,
|
||||
@RequestParam sinceId: String?,
|
||||
@RequestParam maxId: String?,
|
||||
@RequestParam minId: String?
|
||||
): String {
|
||||
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
|
||||
val userDetail = transaction.transaction {
|
||||
userDetailRepository.findByActorId(principal.actorId.id)
|
||||
|
@ -36,7 +41,9 @@ class TimelineController(
|
|||
remoteOnly = false,
|
||||
page = Page.of(
|
||||
maxId = maxId?.toLongOrNull(),
|
||||
sinceId = sinceId?.toLongOrNull()
|
||||
sinceId = sinceId?.toLongOrNull(),
|
||||
minId = minId?.toLongOrNull(),
|
||||
limit = 20
|
||||
)
|
||||
), principal
|
||||
)
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="INFO">
|
||||
<Root level="DEBUG">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
<Logger name="dev.usbharu.owl.broker.service.QueuedTaskAssignerImpl" level="TRACE"/>
|
||||
<Logger name="org.mongodb.driver.cluster" level="WARN"/>
|
||||
<!-- <Logger name="org.mongodb.driver.cluster" level=""/>-->
|
||||
<Logger name="org.apache.tomcat.util.net.NioEndpoint" level="INFO"/>
|
||||
<Logger name="Exposed" level="DEBUG"/>
|
||||
<Logger name="sun.rmi" level="INFO"/>
|
||||
<Logger name="javax.management.remote.rmi" level="INFO"/>
|
||||
</Loggers>
|
||||
</Configuration>
|
|
@ -1,6 +1,8 @@
|
|||
common.audio=\u30AA\u30FC\u30C7\u30A3\u30AA
|
||||
common.audio-download-link=\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9
|
||||
common.empty=\u8868\u793A\u3059\u308B\u3082\u306E\u304C\u3042\u308A\u307E\u305B\u3093
|
||||
common.media-original-link=\u30AA\u30EA\u30B8\u30CA\u30EB
|
||||
common.paging-load=\u3082\u3063\u3068\u898B\u308B
|
||||
common.thumbnail=\u30B5\u30E0\u30CD\u30A4\u30EB
|
||||
common.unknwon-file-type=\u4E0D\u660E\u306A\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F
|
||||
common.video=\u52D5\u753B
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
common.audio=Audio
|
||||
common.audio-download-link=Download the audio.
|
||||
common.empty=Empty
|
||||
common.media-original-link=original
|
||||
common.paging-load=Show more
|
||||
common.thumbnail=thumbnail
|
||||
common.unknwon-file-type=Unknown filetype
|
||||
common.video=Video
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
common.audio=\u30AA\u30FC\u30C7\u30A3\u30AA
|
||||
common.audio-download-link=\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9
|
||||
common.empty=\u8868\u793A\u3059\u308B\u3082\u306E\u304C\u3042\u308A\u307E\u305B\u3093
|
||||
common.media-original-link=\u30AA\u30EA\u30B8\u30CA\u30EB
|
||||
common.paging-load=\u3082\u3063\u3068\u898B\u308B
|
||||
common.thumbnail=\u30B5\u30E0\u30CD\u30A4\u30EB
|
||||
common.unknwon-file-type=\u4E0D\u660E\u306A\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F
|
||||
common.video=\u52D5\u753B
|
||||
|
|
|
@ -7,14 +7,16 @@
|
|||
<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 th:if="${timelineObject.prev != null}">
|
||||
<a th:href="${href + '?minId=' + timelineObject.prev.id}" th:text="#{common.paging-load}">Show more</a>
|
||||
</div>
|
||||
<div th:if="${timelineObject.isEmpty()}" th:text="#{common.empty}"></div>
|
||||
<div th:each="postDetail : ${timelineObject}">
|
||||
<th:block th:replace="fragments-post :: single-simple-post(${postDetail})"></th:block>
|
||||
<th:block th:replace="~{fragments-post :: single-simple-post(${postDetail})}"></th:block>
|
||||
<th:block th:replace="~{fragments-post :: single-post-controller(${postDetail})}"></th:block>
|
||||
</div>
|
||||
<div th:if="${timelineObject.next == null}">
|
||||
<a th:href="${href + '/?sinceId=' + timelineObject.next}"></a>
|
||||
<div th:if="${timelineObject.next != null}">
|
||||
<a th:href="${href + '?maxId=' + timelineObject.next.id}" th:text="#{common.paging-load}">Show more</a>
|
||||
</div>
|
||||
</th:block>
|
||||
</body>
|
||||
|
|
Loading…
Reference in New Issue