1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
use std::collections::HashSet;
use std::collections::BTreeMap;
use anyhow::Result;
use futures::StreamExt;
use iced_native::widget::scrollable::State as ScrollableState;
use crate::app::Message;
use crate::post::Post;
use distrox_lib::client::Client;
use distrox_lib::stream::NodeStreamBuilder;
use distrox_lib::types::Payload;
use distrox_lib::types::DateTime;
#[derive(Debug)]
pub struct Timeline {
post_ids: HashSet<cid::Cid>,
posts: BTreeMap<DateTime, Post>,
scrollable: ScrollableState,
}
impl Timeline {
pub fn new() -> Self {
Self {
post_ids: HashSet::with_capacity(1000),
posts: BTreeMap::new(),
scrollable: ScrollableState::new(),
}
}
pub fn push(&mut self, payload: Payload, content: String) {
if self.post_ids.insert(payload.content()) {
self.posts.insert(payload.timestamp().clone(), Post::new(payload, content));
}
}
pub fn view(&mut self) -> iced::Element<Message> {
let scrollable = iced::Scrollable::new(&mut self.scrollable)
.padding(10)
.spacing(20)
.width(iced::Length::Fill)
.height(iced::Length::Fill)
.on_scroll(move |offset| {
Message::TimelineScrolled(offset)
});
self.posts
.iter()
.rev()
.fold(scrollable, |scrollable, (_, post)| {
scrollable.push(post.view())
})
.into()
}
}
pub struct PostLoadingRecipe {
client: Client,
head: cid::Cid,
}
impl PostLoadingRecipe {
pub fn new(client: Client, head: cid::Cid) -> Self {
Self { client, head }
}
}
// Make sure iced can use our download stream
impl<H, I> iced_native::subscription::Recipe<H, I> for PostLoadingRecipe
where
H: std::hash::Hasher,
{
type Output = Message;
fn hash(&self, state: &mut H) {
use std::hash::Hash;
self.head.to_bytes().hash(state);
}
fn stream(self: Box<Self>, _input: futures::stream::BoxStream<'static, I>) -> futures::stream::BoxStream<'static, Self::Output>
{
log::debug!("Streaming posts starting at HEAD = {:?}", self.head);
Box::pin({
NodeStreamBuilder::starting_from(self.head.clone())
.into_stream(self.client.clone())
.then(move |node| {
let client = self.client.clone();
async move {
let payload = client.get_payload(node?.payload()).await?;
let content = client.get_content_text(payload.content()).await?;
Ok((payload, content))
}
})
.map(|res: Result<_>| match res {
Err(_) => Message::PostLoadingFailed,
Ok(p) => Message::PostLoaded(p),
})
})
}
}
|