From d0ff47525f0ab6acc6911cf1914a313919e2b347 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 20 Jul 2024 16:11:35 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=92=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=82=81?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/model/actor/ActorRepository.kt | 1 + .../core/domain/model/post/PostRepository.kt | 6 + .../TimelineObjectDetail.kt | 53 +++++++ .../model/timelineobject/TimelineObject.kt | 8 + .../TimelineRelationship.kt | 12 +- .../model/userdetails/UserDetailRepository.kt | 1 + .../core/external/timeline/TimelineStore.kt | 5 + .../ExposedActorRepository.kt | 12 ++ .../ExposedPostRepository.kt | 27 ++++ .../ExposedTimelineRelationshipRepository.kt | 4 + .../UserDetailRepositoryImpl.kt | 17 +++ .../MongoInternalTimelineObjectRepository.kt | 13 ++ .../timeline/AbstractTimelineStore.kt | 141 ++++++++++++++++-- .../timeline/DefaultTimelineStore.kt | 42 +++++- .../InternalTimelineObjectRepository.kt | 1 + 15 files changed, 322 insertions(+), 21 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt index 266e1d47..53c8789c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt @@ -21,4 +21,5 @@ interface ActorRepository { suspend fun delete(actor: Actor) suspend fun findById(id: ActorId): Actor? suspend fun findByNameAndDomain(name: String, domain: String): Actor? + suspend fun findAllById(actorIds: List): List } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt index d0807839..674bf8bc 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt @@ -24,6 +24,12 @@ interface PostRepository { suspend fun save(post: Post): Post suspend fun saveAll(posts: List): List suspend fun findById(id: PostId): Post? + suspend fun findAllById(ids: List): List suspend fun findByActorId(id: ActorId, page: Page? = null): PaginationList suspend fun delete(post: Post) + suspend fun findByActorIdAndVisibilityInList( + actorId: ActorId, + visibilityList: List, + of: Page? = null + ): PaginationList } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt new file mode 100644 index 00000000..3a0d15f9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/timelineobjectdetail/TimelineObjectDetail.kt @@ -0,0 +1,53 @@ +package dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail + +import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject +import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectId +import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import java.time.Instant + +data class TimelineObjectDetail( + val id: TimelineObjectId, + val timelineUserDetail: UserDetail, + val post: Post, + val postActor: Actor, + val replyPost: Post?, + val replyPostActor: Actor?, + val repostPost: Post?, + val repostPostActor: Actor?, + val isPureRepost: Boolean, + val lastUpdateAt: Instant, + val hasMediaInRepost: Boolean, + val warnFilter: List +) { + companion object { + fun of( + timelineObject: TimelineObject, + timelineUserDetail: UserDetail, + post: Post, + postActor: Actor, + replyPost: Post?, + replyPostActor: Actor?, + repostPost: Post?, + repostPostActor: Actor?, + warnFilter: List + ): TimelineObjectDetail { + return TimelineObjectDetail( + timelineObject.id, + timelineUserDetail, + post, + postActor, + replyPost, + replyPostActor, + repostPost, + repostPostActor, + timelineObject.isPureRepost, + timelineObject.lastUpdatedAt, + timelineObject.hasMediaInRepost, + warnFilter + ) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt index 42586a73..5f8feba1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt @@ -21,7 +21,9 @@ class TimelineObject( val postActorId: ActorId, val postCreatedAt: Instant, val replyId: PostId?, + val replyActorId: ActorId?, val repostId: PostId?, + val repostActorId: ActorId?, visibility: Visibility, isPureRepost: Boolean, mediaIds: List, @@ -75,6 +77,7 @@ class TimelineObject( timelineObjectId: TimelineObjectId, timeline: Timeline, post: Post, + replyActorId: ActorId?, filterResults: List ): TimelineObject { return TimelineObject( @@ -85,7 +88,9 @@ class TimelineObject( postActorId = post.actorId, postCreatedAt = post.createdAt, replyId = post.replyId, + replyActorId = replyActorId, repostId = null, + repostActorId = null, visibility = post.visibility, isPureRepost = true, mediaIds = post.mediaIds, @@ -101,6 +106,7 @@ class TimelineObject( timelineObjectId: TimelineObjectId, timeline: Timeline, post: Post, + replyActorId: ActorId?, repost: Post, filterResults: List ): TimelineObject { @@ -115,7 +121,9 @@ class TimelineObject( postActorId = post.actorId, postCreatedAt = post.createdAt, replyId = post.replyId, + replyActorId = replyActorId, repostId = repost.id, + repostActorId = repost.actorId, visibility = post.visibility, isPureRepost = repost.mediaIds.isEmpty() && repost.overview == null && diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelinerelationship/TimelineRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelinerelationship/TimelineRelationship.kt index 316292c9..77b621d2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelinerelationship/TimelineRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelinerelationship/TimelineRelationship.kt @@ -6,5 +6,13 @@ import dev.usbharu.hideout.core.domain.model.timeline.TimelineId class TimelineRelationship( val id: TimelineRelationshipId, val timelineId: TimelineId, - val actorId: ActorId -) \ No newline at end of file + val actorId: ActorId, + val visible: Visible +) + +enum class Visible { + PUBLIC, + UNLISTED, + FOLLOWERS, + DIRECT +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt index 08e562bd..d117b79d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt @@ -21,4 +21,5 @@ interface UserDetailRepository { suspend fun delete(userDetail: UserDetail) suspend fun findByActorId(actorId: Long): UserDetail? suspend fun findById(id: Long): UserDetail? + suspend fun findAllById(idList: List): List } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/timeline/TimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/timeline/TimelineStore.kt index 6c3331fe..64b6c0b6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/timeline/TimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/timeline/TimelineStore.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.core.external.timeline import dev.usbharu.hideout.core.domain.model.post.Post +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.timelinerelationship.TimelineRelationship @@ -10,6 +11,10 @@ interface TimelineStore { suspend fun removePost(post: Post) suspend fun addTimelineRelationship(timelineRelationship: TimelineRelationship) suspend fun removeTimelineRelationship(timelineRelationship: TimelineRelationship) + + suspend fun updateTimelineRelationship(timelineRelationship: TimelineRelationship) suspend fun addTimeline(timeline: Timeline, timelineRelationshipList: List) suspend fun removeTimeline(timeline: Timeline) + + suspend fun readTimeline(timeline: Timeline): List } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index 758c2126..96e663e2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -98,6 +98,18 @@ class ExposedActorRepository( } } + override suspend fun findAllById(actorIds: List): List { + return query { + Actors + .leftJoin(ActorsAlsoKnownAs, onColumn = { id }, otherColumn = { actorId }) + .selectAll() + .where { + Actors.id inList actorIds.map { it.id } + } + .let(actorQueryMapper::map) + } + } + companion object { private val logger = LoggerFactory.getLogger(ExposedActorRepository::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index 69bc97b0..e400527c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -162,6 +162,18 @@ class ExposedPostRepository( .first() } + override suspend fun findAllById(ids: List): List { + return query { + Posts + .selectAll() + .where { + Posts.id inList ids.map { it.id } + } + .let(postQueryMapper::map) + } + + } + override suspend fun findByActorId(id: ActorId, page: Page?): PaginationList = PaginationList(query { Posts .selectAll() @@ -180,6 +192,21 @@ class ExposedPostRepository( update(post) } + override suspend fun findByActorIdAndVisibilityInList( + actorId: ActorId, + visibilityList: List, + of: Page? + ): PaginationList { + return PaginationList(query { + Posts + .selectAll() + .where { + Posts.actorId eq actorId.id and (visibility inList visibilityList.map { it.name }) + } + .let(postQueryMapper::map) + }, null, null) + } + companion object { private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRelationshipRepository.kt index 681593ae..abfdd8be 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRelationshipRepository.kt @@ -5,6 +5,7 @@ import dev.usbharu.hideout.core.domain.model.timeline.TimelineId import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipId import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository +import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.slf4j.Logger @@ -22,6 +23,7 @@ class ExposedTimelineRelationshipRepository : AbstractRepository(), TimelineRela it[id] = timelineRelationship.id.value it[timelineId] = timelineRelationship.timelineId.value it[actorId] = timelineRelationship.actorId.id + it[visible] = timelineRelationship.visible.name } } return timelineRelationship @@ -53,6 +55,7 @@ fun ResultRow.toTimelineRelationship(): TimelineRelationship { TimelineRelationshipId(this[TimelineRelationships.id]), TimelineId(this[TimelineRelationships.timelineId]), ActorId(this[TimelineRelationships.actorId]), + Visible.valueOf(this[TimelineRelationships.visible]) ) } @@ -60,5 +63,6 @@ object TimelineRelationships : Table("timeline_relationships") { val id = long("id") val timelineId = long("timeline_id").references(Timelines.id) val actorId = long("actor_id").references(Actors.id) + val visible = varchar("visible", 100) override val primaryKey: PrimaryKey = PrimaryKey(id) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt index 03ff1695..865da28c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt @@ -89,6 +89,23 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() { } } + override suspend fun findAllById(idList: List): List { + return query { + UserDetails + .selectAll() + .where { UserDetails.id inList idList.map { it.id } } + .map { + UserDetail.create( + UserDetailId(it[UserDetails.id]), + ActorId(it[UserDetails.actorId]), + UserDetailHashedPassword(it[UserDetails.password]), + it[UserDetails.autoAcceptFolloweeFollowRequest], + it[UserDetails.lastMigration] + ) + } + } + } + companion object { private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt index dd09daf6..5526687f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt @@ -51,6 +51,11 @@ class MongoInternalTimelineObjectRepository(private val springDataMongoTimelineO springDataMongoTimelineObjectRepository.deleteByTimelineId(timelineId.value) } + override suspend fun findByTimelineId(timelineId: TimelineId): List { + return springDataMongoTimelineObjectRepository.findByTimelineId(timelineId).map { it.toTimelineObject() } + .toList() + } + } @@ -63,7 +68,9 @@ data class SpringDataMongoTimelineObject( val postActorId: Long, val postCreatedAt: Long, val replyId: Long?, + val replyActorId: Long?, val repostId: Long?, + val repostActorId: Long?, val visibility: Visibility, val isPureRepost: Boolean, val mediaIds: List, @@ -83,7 +90,9 @@ data class SpringDataMongoTimelineObject( ActorId(postActorId), Instant.ofEpochSecond(postCreatedAt), replyId?.let { PostId(it) }, + replyActorId?.let { ActorId(it) }, repostId?.let { PostId(it) }, + repostActorId?.let { ActorId(it) }, visibility, isPureRepost, mediaIds.map { MediaId(it) }, @@ -105,7 +114,9 @@ data class SpringDataMongoTimelineObject( timelineObject.postActorId.id, timelineObject.postCreatedAt.epochSecond, timelineObject.replyId?.id, + timelineObject.replyActorId?.id, timelineObject.repostId?.id, + timelineObject.repostActorId?.id, timelineObject.visibility, timelineObject.isPureRepost, timelineObject.mediaIds.map { it.id }, @@ -149,4 +160,6 @@ interface SpringDataMongoTimelineObjectRepository : CoroutineCrudRepository } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt index 6dbe5e6f..5f25d29c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt @@ -1,27 +1,36 @@ package dev.usbharu.hideout.core.infrastructure.timeline +import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.filter.Filter import dev.usbharu.hideout.core.domain.model.filter.FilteredPost import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.Visibility +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.TimelineVisibility import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectId +import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship +import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.external.timeline.TimelineStore +import java.time.Instant abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateService) : TimelineStore { override suspend fun addPost(post: Post) { val timelineList = getTimelines(post.actorId) val repost = post.repostId?.let { getPost(it) } + val replyActorId = post.replyId?.let { getPost(it)?.actorId } - val timelineObjectList = timelineList.map { - createTimelineObject(post, repost, it) + val timelineObjectList = timelineList.mapNotNull { + createTimelineObject(post, replyActorId, repost, it) } insertTimelineObject(timelineObjectList) @@ -31,24 +40,46 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe protected abstract suspend fun getTimeline(timelineId: TimelineId): Timeline? - protected suspend fun createTimelineObject(post: Post, repost: Post?, timeline: Timeline): TimelineObject { + protected suspend fun createTimelineObject( + post: Post, + replyActorId: ActorId?, + repost: Post?, + timeline: Timeline + ): TimelineObject? { + if (post.visibility == Visibility.DIRECT) { + return null + } + if (timeline.visibility == TimelineVisibility.PUBLIC && post.visibility != Visibility.PUBLIC) { + return null + } + if (timeline.visibility == TimelineVisibility.UNLISTED && (post.visibility != Visibility.PUBLIC || post.visibility != Visibility.UNLISTED)) { + return null + } + val filters = getFilters(timeline.userDetailId) val applyFilters = applyFilters(post, filters) if (repost != null) { return TimelineObject.create( - TimelineObjectId(idGenerateService.generateId()), timeline, post, repost, applyFilters.filterResults + TimelineObjectId(idGenerateService.generateId()), + timeline, + post, + replyActorId, + repost, + applyFilters.filterResults ) } return TimelineObject.create( - TimelineObjectId(idGenerateService.generateId()), timeline, post, applyFilters.filterResults + TimelineObjectId(idGenerateService.generateId()), timeline, post, replyActorId, applyFilters.filterResults ) } protected abstract suspend fun getFilters(userDetailId: UserDetailId): List + protected abstract suspend fun getNewerFilters(userDetailId: UserDetailId, lastUpdateAt: Instant): List + protected abstract suspend fun applyFilters(post: Post, filters: List): FilteredPost protected abstract suspend fun getPost(postId: PostId): Post? @@ -65,7 +96,11 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe protected abstract suspend fun removeTimelineObject(timelineId: TimelineId) - protected abstract suspend fun getPosts(timelineRelationshipList: List): List + protected abstract suspend fun getPostsByTimelineRelationshipList(timelineRelationshipList: List): List + + protected abstract suspend fun getPostsByPostId(postIds: List): List + + protected abstract suspend fun getTimelineObject(timelineId: TimelineId): List override suspend fun updatePost(post: Post) { val timelineObjectByPostId = getTimelineObjectByPostId(post.id) @@ -91,33 +126,62 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe updateTimelineObject(timelineObjectList) } - protected abstract suspend fun getActorPost(actorId: ActorId): List + protected abstract suspend fun getActorPost(actorId: ActorId, visibilityList: List): List override suspend fun removePost(post: Post) { removeTimelineObject(post.id) } override suspend fun addTimelineRelationship(timelineRelationship: TimelineRelationship) { - val postList = getActorPost(timelineRelationship.actorId) + val visibilityList = visibilities(timelineRelationship) + val postList = getActorPost(timelineRelationship.actorId, visibilityList) val timeline = getTimeline(timelineRelationship.timelineId) ?: return - val timelineObjects = postList.map { post -> + val timelineObjects = postList.mapNotNull { post -> val repost = post.repostId?.let { getPost(it) } - createTimelineObject(post, repost, timeline) + val replyActorId = post.replyId?.let { getPost(it)?.actorId } + createTimelineObject(post, replyActorId, repost, timeline) } insertTimelineObject(timelineObjects) } + protected fun visibilities(timelineRelationship: TimelineRelationship): List { + val visibilityList = when (timelineRelationship.visible) { + Visible.PUBLIC -> { + listOf(Visibility.PUBLIC) + } + + Visible.UNLISTED -> { + listOf(Visibility.PUBLIC, Visibility.UNLISTED) + } + + Visible.FOLLOWERS -> { + listOf(Visibility.PUBLIC, Visibility.UNLISTED, Visibility.FOLLOWERS) + } + + Visible.DIRECT -> { + listOf(Visibility.PUBLIC, Visibility.UNLISTED, Visibility.FOLLOWERS, Visibility.DIRECT) + } + } + return visibilityList + } + override suspend fun removeTimelineRelationship(timelineRelationship: TimelineRelationship) { removeTimelineObject(timelineRelationship.timelineId, timelineRelationship.actorId) } - override suspend fun addTimeline(timeline: Timeline, timelineRelationshipList: List) { - val postList = getPosts(timelineRelationshipList) + override suspend fun updateTimelineRelationship(timelineRelationship: TimelineRelationship) { + removeTimelineRelationship(timelineRelationship) + addTimelineRelationship(timelineRelationship) + } - val timelineObjectList = postList.map { post -> + override suspend fun addTimeline(timeline: Timeline, timelineRelationshipList: List) { + val postList = getPostsByTimelineRelationshipList(timelineRelationshipList) + + val timelineObjectList = postList.mapNotNull { post -> val repost = post.repostId?.let { getPost(it) } - createTimelineObject(post, repost, timeline) + val replyActorId = post.replyId?.let { getPost(it)?.actorId } + createTimelineObject(post, replyActorId, repost, timeline) } insertTimelineObject(timelineObjectList) @@ -126,4 +190,53 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe override suspend fun removeTimeline(timeline: Timeline) { removeTimelineObject(timeline.id) } + + override suspend fun readTimeline(timeline: Timeline): List { + val timelineObjectList = getTimelineObject(timeline.id) + val lastUpdatedAt = timelineObjectList.minBy { it.lastUpdatedAt }.lastUpdatedAt + + val newerFilters = getNewerFilters(timeline.userDetailId, lastUpdatedAt) + + val posts = + getPostsByPostId(timelineObjectList.map { it.postId } + timelineObjectList.mapNotNull { it.repostId } + timelineObjectList.mapNotNull { it.replyId }) + + val userDetails = getUserDetails(timelineObjectList.map { it.userDetailId }) + + val actors = + getActors(timelineObjectList.map { it.postActorId } + timelineObjectList.mapNotNull { it.repostActorId } + timelineObjectList.mapNotNull { it.replyActorId }) + + val postMap = posts.associate { post -> + post.id to applyFilters(post, newerFilters) + } + + return timelineObjectList.mapNotNull { + val timelineUserDetail = userDetails[it.userDetailId] ?: return@mapNotNull null + val actor = actors[it.postActorId] ?: return@mapNotNull null + val post = postMap[it.postId] ?: return@mapNotNull null + val reply = postMap[it.replyId] + val replyActor = actors[it.replyActorId] + val repost = postMap[it.repostId] + val repostActor = actors[it.repostActorId] + TimelineObjectDetail.of( + timelineObject = it, + timelineUserDetail = timelineUserDetail, + post = post.post, + postActor = actor, + replyPost = reply?.post, + replyPostActor = replyActor, + repostPost = repost?.post, + repostPostActor = repostActor, + warnFilter = it.warnFilters + post.filterResults.map { + TimelineObjectWarnFilter( + it.filter.id, + it.matchedKeyword + ) + } + ) + } + } + + abstract suspend fun getActors(actorIds: List): Map + + abstract suspend fun getUserDetails(userDetailIdList: List): Map } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt index 78b0452c..038125c8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStore.kt @@ -1,7 +1,9 @@ package dev.usbharu.hideout.core.infrastructure.timeline import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig +import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.filter.Filter import dev.usbharu.hideout.core.domain.model.filter.FilterContext import dev.usbharu.hideout.core.domain.model.filter.FilterRepository @@ -9,6 +11,7 @@ import dev.usbharu.hideout.core.domain.model.filter.FilteredPost import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostRepository +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.timeline.Timeline import dev.usbharu.hideout.core.domain.model.timeline.TimelineId @@ -16,10 +19,13 @@ import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import org.springframework.stereotype.Component +import java.time.Instant @Component open class DefaultTimelineStore( @@ -30,7 +36,9 @@ open class DefaultTimelineStore( private val filterDomainService: FilterDomainService, idGenerateService: IdGenerateService, private val defaultTimelineStoreConfig: DefaultTimelineStoreConfig, - private val internalTimelineObjectRepository: InternalTimelineObjectRepository + private val internalTimelineObjectRepository: InternalTimelineObjectRepository, + private val userDetailRepository: UserDetailRepository, + private val actorRepository: ActorRepository ) : AbstractTimelineStore(idGenerateService) { override suspend fun getTimelines(actorId: ActorId): List { return timelineRepository.findByIds( @@ -49,6 +57,10 @@ open class DefaultTimelineStore( return filterRepository.findByUserDetailId(userDetailId) } + override suspend fun getNewerFilters(userDetailId: UserDetailId, lastUpdateAt: Instant): List { + TODO("Not yet implemented") + } + override suspend fun applyFilters(post: Post, filters: List): FilteredPost { return filterDomainService.apply(post, FilterContext.HOME, filters) } @@ -81,11 +93,31 @@ open class DefaultTimelineStore( internalTimelineObjectRepository.deleteByTimelineId(timelineId) } - override suspend fun getPosts(timelineRelationshipList: List): List { - return timelineRelationshipList.map { it.actorId }.flatMap { getActorPost(it) } + override suspend fun getPostsByTimelineRelationshipList(timelineRelationshipList: List): List { + return timelineRelationshipList.flatMap { getActorPost(it.actorId, visibilities(it)) } } - override suspend fun getActorPost(actorId: ActorId): List { - return postRepository.findByActorId(actorId, Page.of(limit = defaultTimelineStoreConfig.actorPostsCount)) + override suspend fun getPostsByPostId(postIds: List): List { + return postRepository.findAllById(postIds) + } + + override suspend fun getTimelineObject(timelineId: TimelineId): List { + return internalTimelineObjectRepository.findByTimelineId(timelineId) + } + + override suspend fun getActorPost(actorId: ActorId, visibilityList: List): List { + return postRepository.findByActorIdAndVisibilityInList( + actorId, + visibilityList, + Page.of(limit = defaultTimelineStoreConfig.actorPostsCount) + ) + } + + override suspend fun getActors(actorIds: List): Map { + return actorRepository.findAllById(actorIds).associateBy { it.id } + } + + override suspend fun getUserDetails(userDetailIdList: List): Map { + return userDetailRepository.findAllById(userDetailIdList).associateBy { it.id } } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/InternalTimelineObjectRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/InternalTimelineObjectRepository.kt index ce554b46..c0a434ad 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/InternalTimelineObjectRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/InternalTimelineObjectRepository.kt @@ -17,4 +17,5 @@ interface InternalTimelineObjectRepository { suspend fun deleteByTimelineIdAndActorId(timelineId: TimelineId, actorId: ActorId) suspend fun deleteByTimelineId(timelineId: TimelineId) + suspend fun findByTimelineId(timelineId: TimelineId): List } \ No newline at end of file