feat: implement unread notification count and enhance user listing with pagination
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Failing after 9m33s
test / unit (pull_request) Successful in 16m24s
test / integration (pull_request) Failing after 16m52s

This commit is contained in:
2026-05-15 12:04:00 +02:00
parent 5f61a71336
commit 6273635aeb
15 changed files with 253 additions and 154 deletions

View File

@@ -1,8 +1,20 @@
use domain::{
errors::DomainError, models::remote_actor::RemoteActor, ports::FederationActionPort,
errors::DomainError,
events::DomainEvent,
models::{
actor_connection_summary::ActorConnectionSummary,
feed::{FeedEntry, PageParams, Paginated},
remote_actor::RemoteActor,
},
ports::{
ActivityPubRepository, EventPublisher, FederationActionPort, FeedRepository,
FollowRepository, RemoteActorConnectionRepository, UserRepository,
},
value_objects::UserId,
};
use super::social;
pub async fn list_pending_requests(
federation: &dyn FederationActionPort,
user_id: &UserId,
@@ -48,6 +60,87 @@ pub async fn list_remote_following(
federation.get_remote_following(user_id).await
}
pub async fn remove_remote_following(
follows: &dyn FollowRepository,
users: &dyn UserRepository,
federation: &dyn FederationActionPort,
events: &dyn EventPublisher,
user_id: &UserId,
handle: &str,
) -> Result<(), DomainError> {
social::unfollow_actor(follows, users, federation, events, user_id, handle).await
}
pub async fn get_remote_actor_posts(
federation: &dyn FederationActionPort,
ap_repo: &dyn ActivityPubRepository,
feed: &dyn FeedRepository,
events: &dyn EventPublisher,
handle: &str,
page: PageParams,
viewer_id: Option<&UserId>,
) -> Result<Paginated<FeedEntry>, DomainError> {
let actor = federation.lookup_actor(handle).await?;
let ap_url = url::Url::parse(&actor.url).map_err(|e| DomainError::Internal(e.to_string()))?;
let author_id = match ap_repo.find_remote_actor_id(&ap_url).await? {
Some(id) => id,
None => ap_repo.intern_remote_actor(&ap_url).await?,
};
let result = feed.user_feed(&author_id, &page, viewer_id).await?;
if let Some(outbox_url) = actor.outbox_url {
let _ = events
.publish(&DomainEvent::FetchRemoteActorPosts {
actor_ap_url: actor.url,
outbox_url,
})
.await;
}
Ok(result)
}
const ACTOR_CONNECTIONS_CACHE_TTL_SECS: i64 = 3600;
pub async fn get_actor_connections_page(
federation: &dyn FederationActionPort,
connections: &dyn RemoteActorConnectionRepository,
events: &dyn EventPublisher,
handle: &str,
connection_type: &str,
page: u32,
) -> Result<(Vec<ActorConnectionSummary>, bool), DomainError> {
const PAGE_SIZE: usize = 20;
let actor = federation.lookup_actor(handle).await?;
let collection_url = match connection_type {
"followers" => actor.followers_url.ok_or(DomainError::NotFound)?,
_ => actor.following_url.ok_or(DomainError::NotFound)?,
};
let items = connections
.list_connections(&actor.url, connection_type, page)
.await?;
let stale = match connections
.connection_page_age(&actor.url, connection_type, page)
.await?
{
None => true,
Some(age) => {
chrono::Utc::now().signed_duration_since(age).num_seconds()
> ACTOR_CONNECTIONS_CACHE_TTL_SECS
}
};
if stale {
let _ = events
.publish(&DomainEvent::FetchActorConnections {
actor_ap_url: actor.url,
collection_url,
connection_type: connection_type.to_string(),
page,
})
.await;
}
let has_more = items.len() >= PAGE_SIZE;
Ok((items, has_more))
}
#[cfg(test)]
mod tests {
use super::*;