diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index e3c47e6e..1db8bec2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedquery +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList +import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments import dev.usbharu.hideout.core.infrastructure.exposedrepository.* @@ -108,6 +111,57 @@ class StatusQueryServiceImpl : StatusQueryService { return resolveReplyAndRepost(pairs) } + override suspend fun accountsStatus( + accountId: Long, + onlyMedia: Boolean, + excludeReplies: Boolean, + excludeReblogs: Boolean, + pinned: Boolean, + tagged: String?, + includeFollowers: Boolean, + page: Page + ): PaginationList { + val query = Posts + .leftJoin(PostsMedia) + .leftJoin(Actors) + .leftJoin(Media) + .select { Posts.actorId eq accountId } + + query.pagination(page, Posts.id) + + if (onlyMedia) { + query.andWhere { PostsMedia.mediaId.isNotNull() } + } + if (excludeReplies) { + query.andWhere { Posts.replyId.isNotNull() } + } + if (excludeReblogs) { + query.andWhere { Posts.repostId.isNotNull() } + } + if (includeFollowers) { + query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal, private.ordinal) } + } else { + query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal) } + } + + val pairs = query.groupBy { it[Posts.id] } + .map { it.value } + .map { + toStatus(it.first()).copy( + mediaAttachments = it.mapNotNull { resultRow -> + resultRow.toMediaOrNull()?.toMediaAttachments() + } + ) to it.first()[Posts.repostId] + } + + val statuses = resolveReplyAndRepost(pairs) + return PaginationList( + statuses, + statuses.lastOrNull()?.id?.toLongOrNull(), + statuses.firstOrNull()?.id?.toLongOrNull() + ) + } + override suspend fun findByPostId(id: Long): Status { val map = Posts .leftJoin(PostsMedia) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 6d69423a..92e5e7f4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -65,20 +65,31 @@ class MastodonAccountApiController( tagged: String? ): ResponseEntity> = runBlocking { val userid = loginUserContextHolder.getLoginUserId() - val statusFlow = accountApiService.accountsStatuses( + val statuses = accountApiService.accountsStatuses( userid = id.toLong(), - maxId = maxId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - limit = limit, onlyMedia = onlyMedia, excludeReplies = excludeReplies, excludeReblogs = excludeReblogs, pinned = pinned, tagged = tagged, - loginUser = userid - ).asFlow() - ResponseEntity.ok(statusFlow) + loginUser = userid, + page = Page.of( + maxId?.toLongOrNull(), + sinceId?.toLongOrNull(), + minId?.toLongOrNull(), + limit.coerceIn(0, 80) ?: 40 + ) + ) + val httpHeader = statuses.toHttpHeader( + { "${applicationConfig.url}/api/v1/follow_requests?max_id=$it" }, + { "${applicationConfig.url}/api/v1/follow_requests?min_id=$it" }, + ) + + if (httpHeader != null) { + return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(statuses.asFlow()) + } + + ResponseEntity.ok(statuses.asFlow()) } override fun apiV1AccountsRelationshipsGet( diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt index fe78a70d..39869beb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.mastodon.query +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery @@ -37,5 +39,16 @@ interface StatusQueryService { includeFollowers: Boolean = false ): List + suspend fun accountsStatus( + accountId: Long, + onlyMedia: Boolean = false, + excludeReplies: Boolean = false, + excludeReblogs: Boolean = false, + pinned: Boolean = false, + tagged: String?, + includeFollowers: Boolean = false, + page: Page + ): PaginationList + suspend fun findByPostId(id: Long): Status } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 5357effb..7aef09cd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -34,6 +34,17 @@ interface AccountApiService { loginUser: Long? ): List + suspend fun accountsStatuses( + userid: Long, + onlyMedia: Boolean, + excludeReplies: Boolean, + excludeReblogs: Boolean, + pinned: Boolean, + tagged: String?, + loginUser: Long?, + page: Page + ): PaginationList + suspend fun verifyCredentials(userid: Long): CredentialAccount suspend fun registerAccount(userCreateDto: UserCreateDto): Unit suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship @@ -115,6 +126,38 @@ class AccountApiServiceImpl( } } + override suspend fun accountsStatuses( + userid: Long, + onlyMedia: Boolean, + excludeReplies: Boolean, + excludeReblogs: Boolean, + pinned: Boolean, + tagged: String?, + loginUser: Long?, + page: Page + ): PaginationList { + val canViewFollowers = if (loginUser == null) { + false + } else { + transaction.transaction { + isFollowing(loginUser, userid) + } + } + + return transaction.transaction { + statusQueryService.accountsStatus( + accountId = userid, + onlyMedia = onlyMedia, + excludeReplies = excludeReplies, + excludeReblogs = excludeReblogs, + pinned = pinned, + tagged = tagged, + includeFollowers = canViewFollowers, + page = page + ) + } + } + override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction { val account = accountService.findById(userid) from(account)