refactor: Feed uses SocialIdentity matching, deps From impl, remove get_accepted_following_urls

Feed builds FollowingFilter by matching SocialIdentity::Local/Remote
instead of URL-prefix heuristic. Removed get_accepted_following_urls
from SocialQuery (no longer needed). Added From<&AppState> for deps
structs — 20 construction sites collapsed to one-liners.
This commit is contained in:
2026-07-10 16:31:05 +02:00
parent 7e02f15a85
commit 46b8488b09
7 changed files with 87 additions and 184 deletions

View File

@@ -6,7 +6,7 @@ use domain::{
FeedEntry,
collections::{PageParams, Paginated},
},
value_objects::UserId,
value_objects::{SocialIdentity, UserId},
};
pub async fn execute(
@@ -36,28 +36,24 @@ async fn build_following_filter(
}
let viewer_id = query.viewer_user_id?;
let viewer = UserId::from_uuid(viewer_id);
let urls = deps
let actors = deps
.social_query
.get_accepted_following_urls(&viewer)
.get_following(&viewer)
.await
.unwrap_or_default();
if urls.is_empty() {
if actors.is_empty() {
return Some(FollowingFilter {
local_user_ids: vec![viewer_id],
remote_actor_urls: vec![],
});
}
let base_url = &deps.config.base_url;
let mut local_ids = vec![viewer_id];
let mut remote_urls = Vec::new();
for url in urls {
if let Some(suffix) = url.strip_prefix(&format!("{}/users/", base_url))
&& let Ok(parsed_id) = uuid::Uuid::parse_str(suffix)
{
local_ids.push(parsed_id);
continue;
for actor in actors {
match actor.identity {
SocialIdentity::Local(uid) => local_ids.push(uid.value()),
SocialIdentity::Remote { actor_url } => remote_urls.push(actor_url),
}
remote_urls.push(url);
}
Some(FollowingFilter {
local_user_ids: local_ids,

View File

@@ -3,7 +3,7 @@ use std::sync::Arc;
use async_trait::async_trait;
use domain::errors::DomainError;
use domain::testing::InMemorySocialRepository;
use domain::value_objects::SocialActor;
use domain::value_objects::{SocialActor, SocialIdentity, UserId};
use crate::{
config::AppConfig, diary::deps::GetActivityFeedDeps, diary::get_activity_feed,
@@ -63,72 +63,59 @@ async fn returns_feed_with_following_filter() {
assert!(result.items.is_empty());
}
struct FakeSocialWithFollowing(Vec<String>);
struct FakeSocialWithFollowing(Vec<SocialActor>);
#[async_trait]
impl domain::ports::SocialQuery for FakeSocialWithFollowing {
async fn get_following(
&self,
_: &domain::value_objects::UserId,
_: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
Ok(vec![])
}
async fn get_followers(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<SocialActor>, DomainError> {
Ok(vec![])
}
async fn get_pending_followers(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<SocialActor>, DomainError> {
Ok(vec![])
}
async fn count_following(
&self,
_: &domain::value_objects::UserId,
) -> Result<usize, DomainError> {
Ok(0)
}
async fn count_followers(
&self,
_: &domain::value_objects::UserId,
) -> Result<usize, DomainError> {
Ok(0)
}
async fn get_blocked(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<SocialActor>, DomainError> {
Ok(vec![])
}
async fn is_following(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
) -> Result<bool, DomainError> {
Ok(false)
}
async fn get_accepted_following_urls(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<String>, DomainError> {
Ok(self.0.clone())
}
async fn get_followers(&self, _: &UserId) -> Result<Vec<SocialActor>, DomainError> {
Ok(vec![])
}
async fn get_pending_followers(&self, _: &UserId) -> Result<Vec<SocialActor>, DomainError> {
Ok(vec![])
}
async fn count_following(&self, _: &UserId) -> Result<usize, DomainError> {
Ok(0)
}
async fn count_followers(&self, _: &UserId) -> Result<usize, DomainError> {
Ok(0)
}
async fn get_blocked(&self, _: &UserId) -> Result<Vec<SocialActor>, DomainError> {
Ok(vec![])
}
async fn is_following(&self, _: &UserId, _: &SocialIdentity) -> Result<bool, DomainError> {
Ok(false)
}
}
#[tokio::test]
async fn following_filter_parses_local_and_remote_urls() {
async fn following_filter_separates_local_and_remote() {
let viewer = uuid::Uuid::new_v4();
let local_friend = uuid::Uuid::new_v4();
let following_urls = vec![
format!("http://localhost:3000/users/{}", local_friend),
"https://remote.example/actor/1".to_string(),
let following = vec![
SocialActor {
identity: SocialIdentity::Local(UserId::from_uuid(local_friend)),
handle: "friend".into(),
display_name: None,
avatar_url: None,
},
SocialActor {
identity: SocialIdentity::Remote {
actor_url: "https://remote.example/actor/1".into(),
},
handle: "@alice@remote.example".into(),
display_name: None,
avatar_url: None,
},
];
let social = Arc::new(FakeSocialWithFollowing(following_urls));
let social = Arc::new(FakeSocialWithFollowing(following));
let deps = GetActivityFeedDeps {
diary: domain::testing::FakeDiaryQuery::new() as _,