1use crate::moderation::ModerationPrefs;
3use atrium_api::app::bsky::actor::defs::SavedFeed;
4use atrium_api::types::Object;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
10#[serde(rename_all = "camelCase")]
11pub struct FeedViewPreferenceData {
12 pub hide_replies: bool,
13 pub hide_replies_by_unfollowed: bool,
14 pub hide_replies_by_like_count: i64,
15 pub hide_reposts: bool,
16 pub hide_quote_posts: bool,
17}
18
19impl Default for FeedViewPreferenceData {
20 fn default() -> Self {
21 Self {
22 hide_replies: false,
23 hide_replies_by_unfollowed: true,
24 hide_replies_by_like_count: 0,
25 hide_reposts: false,
26 hide_quote_posts: false,
27 }
28 }
29}
30
31pub type FeedViewPreference = Object<FeedViewPreferenceData>;
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
35#[serde(rename_all = "camelCase")]
36pub struct ThreadViewPreferenceData {
37 pub sort: String,
38 pub prioritize_followed_users: bool,
39}
40
41impl ThreadViewPreferenceData {
42 pub const SORT_OLDEST: &'static str = "oldest";
43 pub const SORT_NEWEST: &'static str = "newest";
44 pub const SORT_MOST_LIKES: &'static str = "most-likes";
45 pub const SORT_RANDOM: &'static str = "random";
46}
47
48impl Default for ThreadViewPreferenceData {
49 fn default() -> Self {
50 Self { sort: Self::SORT_OLDEST.to_string(), prioritize_followed_users: true }
51 }
52}
53
54pub type ThreadViewPreference = Object<ThreadViewPreferenceData>;
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
58#[serde(rename_all = "camelCase")]
59pub struct Preferences {
60 pub saved_feeds: Vec<SavedFeed>,
61 pub feed_view_prefs: HashMap<String, FeedViewPreference>,
62 pub thread_view_prefs: ThreadViewPreference,
63 pub moderation_prefs: ModerationPrefs,
64}
65
66impl Default for Preferences {
67 fn default() -> Self {
68 Self {
69 saved_feeds: Default::default(),
70 feed_view_prefs: Default::default(),
71 thread_view_prefs: ThreadViewPreferenceData::default().into(),
72 moderation_prefs: Default::default(),
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use crate::moderation::ModerationPrefsLabeler;
81 use atrium_api::app::bsky::actor::get_preferences::Output;
82 use serde_json::{from_str, to_string, Value};
83
84 const XRPC_PREFERENCES_JSON: &str = r#"{
85 "preferences": [
86 {
87 "$type": "app.bsky.actor.defs#savedFeedsPrefV2",
88 "items": [
89 {
90 "id": "3kt2b4tp6gs2a",
91 "pinned": true,
92 "type": "timeline",
93 "value": "following"
94 }
95 ]
96 },
97 {
98 "$type": "app.bsky.actor.defs#threadViewPref",
99 "sort": "oldest",
100 "lab_treeViewEnabled": false
101 },
102 {
103 "$type": "app.bsky.actor.defs#feedViewPref",
104 "feed": "home",
105 "hideRepliesByUnfollowed": false,
106 "lab_mergeFeedEnabled": true
107 }
108 ]
109}"#;
110
111 #[test]
112 fn xrpc_preferences_json() {
113 let deserialized1 = from_str::<Output>(XRPC_PREFERENCES_JSON)
114 .expect("deserializing preferences should succeed");
115 assert_eq!(deserialized1.preferences.len(), 3);
116 let serialized = to_string(&deserialized1).expect("serializing preferences should succeed");
117 assert_eq!(
118 serialized.replace(char::is_whitespace, ""),
119 XRPC_PREFERENCES_JSON.replace(char::is_whitespace, "")
120 );
121 let deserialized2 =
122 from_str::<Output>(&serialized).expect("deserializing preferences should succeed");
123 assert_eq!(deserialized1, deserialized2);
124 }
125
126 #[test]
127 fn sdk_preferences_json() {
128 let preferences = Preferences {
129 saved_feeds: Vec::new(),
130 feed_view_prefs: HashMap::new(),
131 thread_view_prefs: ThreadViewPreferenceData::default().into(),
132 moderation_prefs: ModerationPrefs {
133 labelers: vec![
134 ModerationPrefsLabeler::default(),
135 ModerationPrefsLabeler {
136 did: "did:fake:labeler.test".parse().expect("invalid did"),
137 labels: HashMap::new(),
138 is_default_labeler: false,
139 },
140 ],
141 ..Default::default()
142 },
143 };
144 let serialized1 = to_string(&preferences).expect("serializing preferences should succeed");
145 let deserialized = from_str::<Preferences>(&serialized1)
146 .expect("deserializing preferences should succeed");
147 assert_eq!(preferences, deserialized);
148 let serialized2 = to_string(&deserialized).expect("serializing preferences should succeed");
149 assert_eq!(
150 from_str::<Value>(&serialized1).expect("deserializing to value should succeed"),
151 from_str::<Value>(&serialized2).expect("deserializing to value should succeed"),
152 );
153 }
154}