Skip to main content

egui_charts/services/
profiles.rs

1//! Profiles of other users and the follow relations the host keeps.
2
3use super::Events;
4use crate::data::{MaybeSend, Reply};
5
6/// The host's identifier of a profile.
7#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
8pub struct ProfileId(
9    /// The host's own identifier, unique among its profiles.
10    pub String,
11);
12
13/// What the host knows about a user the signed-in user can follow.
14#[derive(Clone, Debug, PartialEq)]
15#[non_exhaustive]
16pub struct Profile {
17    /// The host's identifier.
18    pub id: ProfileId,
19    /// Display name.
20    pub name: String,
21    /// Short description the user wrote. May be empty.
22    pub bio: String,
23    /// URI of the user's picture, for egui's installed image loaders.
24    pub avatar: Option<String>,
25    /// How many users follow this one, when the host reports it.
26    pub followers: Option<u64>,
27    /// Whether the signed-in user follows this user, as the host last reported.
28    pub followed: bool,
29}
30
31impl Profile {
32    /// A profile that is not followed, without a bio, picture or follower count.
33    pub fn new(id: ProfileId, name: impl Into<String>) -> Self {
34        Self {
35            id,
36            name: name.into(),
37            bio: String::new(),
38            avatar: None,
39            followers: None,
40            followed: false,
41        }
42    }
43}
44
45/// The host's profiles and follow relations. The host keeps the account, decides who may follow
46/// whom and what a profile shows; the library shows what the host pushes and sends the user's
47/// follow and unfollow requests through [`follow`](Self::follow). It never changes
48/// [`Profile::followed`] itself.
49///
50/// Methods are called on the UI thread and must return at once; answer and push from any
51/// thread, runtime or browser task.
52///
53/// ```
54/// use std::sync::Mutex;
55///
56/// use egui_charts::data::Reply;
57/// use egui_charts::services::{Events, Profile, ProfileEvent, ProfileId, Profiles};
58///
59/// /// One profile that accepts every follow request. A real host asks its server.
60/// #[derive(Default)]
61/// struct Local(Mutex<Option<Events<ProfileEvent>>>);
62///
63/// impl Profiles for Local {
64///     fn watch(&self, events: Events<ProfileEvent>) {
65///         events.push(ProfileEvent::Upsert(Profile::new(ProfileId("1".into()), "Ada")));
66///         *self.0.lock().unwrap() = Some(events);
67///     }
68///
69///     fn follow(&self, id: ProfileId, follow: bool, reply: Reply<()>) {
70///         if let Some(events) = &*self.0.lock().unwrap() {
71///             let mut profile = Profile::new(id, "Ada");
72///             profile.followed = follow;
73///             events.push(ProfileEvent::Upsert(profile));
74///         }
75///         reply.send(Ok(()));
76///     }
77/// }
78/// ```
79pub trait Profiles: MaybeSend + 'static {
80    /// Pushes the profiles the user can see, then every change until `events` is inactive. Called
81    /// when the directory starts and again after a lagged stream, each time with a new stream that
82    /// starts from no profiles. The stream takes at most 10,000 pushes between two frames.
83    fn watch(&self, events: Events<ProfileEvent>);
84
85    /// Follows `id` when `follow` is true, otherwise unfollows it. Push the profile with its new
86    /// [`Profile::followed`] as [`ProfileEvent::Upsert`] first, then answer `Ok` once the relation
87    /// changed, or with a [`FeedError`](crate::data::FeedError) the user can read if the host
88    /// refused. Authorization belongs to the host: refuse a request the signed-in user may not
89    /// make.
90    fn follow(&self, id: ProfileId, follow: bool, reply: Reply<()>);
91}
92
93/// A change to the host's profiles, pushed through the stream [`Profiles::watch`] received.
94#[derive(Clone, Debug, PartialEq)]
95#[non_exhaustive]
96pub enum ProfileEvent {
97    /// Adds the profile, or replaces the one with its id.
98    Upsert(Profile),
99    /// Removes the profile with this id, if any.
100    Remove(ProfileId),
101    /// Removes every profile, so a host that reconnects can push them again.
102    Clear,
103}