feat: per-entity federation privacy toggles for reviews and watchlist
- add federate_reviews + federate_watchlist to UserSettings (default true) - new UserFederationSettingsQuery port with FederationFlags struct - remove get_user_federate_goals from LocalApContentQuery - gate ReviewLogged, ReviewUpdated, WatchlistEntryAdded, on_poster_synced on flags - goals gating migrated to UserFederationSettingsQuery - ReviewDeleted and WatchlistEntryRemoved ungated (tombstones always fire) - sqlite + postgres migrations and adapter impls - settings API and SPA toggles
This commit is contained in:
@@ -4,27 +4,34 @@ use crate::value_objects::UserId;
|
||||
pub struct UserSettings {
|
||||
user_id: UserId,
|
||||
federate_goals: bool,
|
||||
federate_reviews: bool,
|
||||
federate_watchlist: bool,
|
||||
}
|
||||
|
||||
impl UserSettings {
|
||||
pub fn new(user_id: UserId) -> Self {
|
||||
Self {
|
||||
user_id,
|
||||
federate_goals: false,
|
||||
federate_goals: true,
|
||||
federate_reviews: true,
|
||||
federate_watchlist: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_persistence(user_id: UserId, federate_goals: bool) -> Self {
|
||||
pub fn from_persistence(
|
||||
user_id: UserId,
|
||||
federate_goals: bool,
|
||||
federate_reviews: bool,
|
||||
federate_watchlist: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
user_id,
|
||||
federate_goals,
|
||||
federate_reviews,
|
||||
federate_watchlist,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_federate_goals(&mut self, value: bool) {
|
||||
self.federate_goals = value;
|
||||
}
|
||||
|
||||
pub fn user_id(&self) -> &UserId {
|
||||
&self.user_id
|
||||
}
|
||||
@@ -32,4 +39,24 @@ impl UserSettings {
|
||||
pub fn federate_goals(&self) -> bool {
|
||||
self.federate_goals
|
||||
}
|
||||
|
||||
pub fn set_federate_goals(&mut self, value: bool) {
|
||||
self.federate_goals = value;
|
||||
}
|
||||
|
||||
pub fn federate_reviews(&self) -> bool {
|
||||
self.federate_reviews
|
||||
}
|
||||
|
||||
pub fn set_federate_reviews(&mut self, value: bool) {
|
||||
self.federate_reviews = value;
|
||||
}
|
||||
|
||||
pub fn federate_watchlist(&self) -> bool {
|
||||
self.federate_watchlist
|
||||
}
|
||||
|
||||
pub fn set_federate_watchlist(&mut self, value: bool) {
|
||||
self.federate_watchlist = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,6 +456,17 @@ pub trait UserSettingsRepository: Send + Sync {
|
||||
async fn save(&self, settings: &UserSettings) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
pub struct FederationFlags {
|
||||
pub goals: bool,
|
||||
pub reviews: bool,
|
||||
pub watchlist: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserFederationSettingsQuery: Send + Sync {
|
||||
async fn get_federation_flags(&self, user_id: &UserId) -> Result<FederationFlags, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait RemoteGoalRepository: Send + Sync {
|
||||
async fn save(&self, entry: RemoteGoalEntry) -> Result<(), DomainError>;
|
||||
@@ -502,8 +513,6 @@ pub trait LocalApContentQuery: Send + Sync {
|
||||
limit: usize,
|
||||
) -> Result<Vec<DiaryEntry>, DomainError>;
|
||||
|
||||
async fn get_user_federate_goals(&self, user_id: &UserId) -> Result<bool, DomainError>;
|
||||
|
||||
async fn get_goal_with_progress(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
|
||||
@@ -18,9 +18,10 @@ use crate::{
|
||||
collections::{PageParams, Paginated},
|
||||
},
|
||||
ports::{
|
||||
GoalRepository, ImportProfileRepository, ImportSessionRepository, MovieProfileRepository,
|
||||
MovieRepository, RefreshSessionRepository, ReviewRepository, UserProfileFieldsRepository,
|
||||
UserRepository, UserSettingsRepository, WatchEventRepository, WatchlistRepository,
|
||||
FederationFlags, GoalRepository, ImportProfileRepository, ImportSessionRepository,
|
||||
MovieProfileRepository, MovieRepository, RefreshSessionRepository, ReviewRepository,
|
||||
UserFederationSettingsQuery, UserProfileFieldsRepository, UserRepository,
|
||||
UserSettingsRepository, WatchEventRepository, WatchlistRepository,
|
||||
WebhookTokenRepository,
|
||||
},
|
||||
value_objects::{
|
||||
@@ -441,6 +442,22 @@ impl UserSettingsRepository for InMemoryUserSettingsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UserFederationSettingsQuery for InMemoryUserSettingsRepository {
|
||||
async fn get_federation_flags(&self, user_id: &UserId) -> Result<FederationFlags, DomainError> {
|
||||
let store = self.store.lock().unwrap();
|
||||
let settings = store
|
||||
.get(&user_id.value())
|
||||
.cloned()
|
||||
.unwrap_or_else(|| UserSettings::new(user_id.clone()));
|
||||
Ok(FederationFlags {
|
||||
goals: settings.federate_goals(),
|
||||
reviews: settings.federate_reviews(),
|
||||
watchlist: settings.federate_watchlist(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ── InMemoryWebhookTokenRepository ──────────────────────────────────────────
|
||||
|
||||
pub struct InMemoryWebhookTokenRepository {
|
||||
|
||||
Reference in New Issue
Block a user