aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbryan newbold <bnewbold@robocracy.org>2023-02-20 02:02:59 -0800
committerbryan newbold <bnewbold@robocracy.org>2023-02-20 02:02:59 -0800
commitb1b437ce1f83ebfb8808964846c964cac5e907c2 (patch)
tree30fe900a75eb9e0b6a8ec4346dd1c66ed3581798
parentb1af748739fadce312aafc1e5f09ced891c326b2 (diff)
downloadadenosine-b1b437ce1f83ebfb8808964846c964cac5e907c2.tar.gz
adenosine-b1b437ce1f83ebfb8808964846c964cac5e907c2.zip
app_bsky: expand post (and post view) types
-rw-r--r--adenosine-pds/src/db_bsky.rs184
-rw-r--r--adenosine-pds/src/web.rs6
-rw-r--r--adenosine-pds/templates/account.html2
-rw-r--r--adenosine-pds/templates/macro.html10
-rw-r--r--adenosine-pds/templates/thread.html6
-rw-r--r--adenosine/src/app_bsky/mod.rs140
6 files changed, 226 insertions, 122 deletions
diff --git a/adenosine-pds/src/db_bsky.rs b/adenosine-pds/src/db_bsky.rs
index 834053e..5e968af 100644
--- a/adenosine-pds/src/db_bsky.rs
+++ b/adenosine-pds/src/db_bsky.rs
@@ -59,7 +59,7 @@ pub fn bsky_mutate_db(db: &mut AtpDatabase, did: &Did, mutations: Vec<Mutation>)
}
// TODO: should probably return Result<Option<Profile>>?
-pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::Profile> {
+pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::ProfileView> {
// first get the profile record
let mut profile_cid: Option<Cid> = None;
let commit_cid = match srv.repo.lookup_commit(did)? {
@@ -106,7 +106,7 @@ pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::Pro
actorType: "app.bsky.system.actorUser".to_string(),
cid: "bafyreid27zk7lbis4zw5fz4podbvbs4fc5ivwji3dmrwa6zggnj4bnd57u".to_string(),
};
- Ok(app_bsky::Profile {
+ Ok(app_bsky::ProfileView {
did: did.to_string(),
handle,
creator: did.to_string(),
@@ -117,7 +117,7 @@ pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::Pro
followsCount: follows_count,
postsCount: post_count,
membersCount: 0,
- myState: json!({}),
+ viewer: json!({}),
})
}
@@ -177,8 +177,9 @@ fn feed_row(row: &rusqlite::Row) -> Result<FeedRow> {
})
}
-fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result<app_bsky::FeedItem> {
+fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result<app_bsky::FeedPostView> {
let record_ipld = srv.repo.get_ipld(&row.item_post_cid)?;
+ let post_record: app_bsky::Post = serde_json::from_value(ipld_into_json_value(record_ipld))?;
let uri = format!(
"at://{}/{}/{}",
row.item_did, "app.bsky.feed.post", row.item_post_tid
@@ -200,29 +201,42 @@ fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result<app_bsky::Feed
.prepare_cached("SELECT COUNT(*) FROM bsky_post WHERE reply_to_parent_uri = $1")?;
let reply_count: u64 = stmt.query_row(params!(uri), |row| row.get(0))?;
- let feed_item = app_bsky::FeedItem {
- uri,
- cid: row.item_post_cid.to_string(),
- author: app_bsky::User {
- did: row.item_did.to_string(),
- handle: row.item_handle,
- displayName: None, // TODO: fetch from profile (or cache)
+ let decl = app_bsky::DeclRef {
+ actorType: "app.bsky.system.actorUser".to_string(),
+ cid: "bafyreid27zk7lbis4zw5fz4podbvbs4fc5ivwji3dmrwa6zggnj4bnd57u".to_string(),
+ };
+ let feed_item = app_bsky::FeedPostView {
+ post: app_bsky::PostView {
+ uri,
+ cid: row.item_post_cid.to_string(),
+ author: app_bsky::UserView {
+ did: row.item_did.to_string(),
+ handle: row.item_handle,
+ declaration: decl,
+ // TODO:
+ displayName: None,
+ avatar: None,
+ viewer: None,
+ },
+ repostedBy: None,
+ record: post_record,
+ embed: None,
+ replyCount: reply_count,
+ repostCount: repost_count,
+ upvoteCount: like_count,
+ downvoteCount: 0,
+ indexedAt: row.indexed_at,
+ viewer: None,
},
- repostedBy: None,
- record: ipld_into_json_value(record_ipld),
- embed: None,
- replyCount: reply_count,
- repostCount: repost_count,
- upvoteCount: like_count,
- downvoteCount: 0,
- indexedAt: row.indexed_at,
- myState: None,
+ // TODO:
+ reason: None,
+ reply: None,
};
Ok(feed_item)
}
pub fn bsky_get_timeline(srv: &mut AtpService, did: &Did) -> Result<app_bsky::GenericFeed> {
- let mut feed: Vec<app_bsky::FeedItem> = vec![];
+ let mut feed: Vec<app_bsky::FeedPostView> = vec![];
// TODO: also handle reposts
let rows = {
let mut stmt = srv.atp_db
@@ -243,7 +257,7 @@ pub fn bsky_get_timeline(srv: &mut AtpService, did: &Did) -> Result<app_bsky::Ge
}
pub fn bsky_get_author_feed(srv: &mut AtpService, did: &Did) -> Result<app_bsky::GenericFeed> {
- let mut feed: Vec<app_bsky::FeedItem> = vec![];
+ let mut feed: Vec<app_bsky::FeedPostView> = vec![];
// TODO: also handle reposts
let rows = {
let mut stmt = srv.atp_db
@@ -285,7 +299,7 @@ pub fn bsky_get_thread(
_ => Err(anyhow!("expected a record in uri: {}", uri))?,
};
- // post itself, as a app_bsky::FeedItem
+ // post itself, as a app_bsky::FeedPostView
let post_items = {
let mut stmt = srv.atp_db
.conn
@@ -321,40 +335,47 @@ pub fn bsky_get_thread(
rows
};
for row in rows {
- let item = feed_row_to_item(srv, row)?;
- children.push(app_bsky::ThreadItem {
- uri: item.uri,
- cid: item.cid,
- author: item.author,
- record: item.record,
- embed: item.embed,
+ let item = feed_row_to_item(srv, row)?.post;
+ children.push(app_bsky::ThreadPostView {
+ post: app_bsky::PostView {
+ uri: item.uri,
+ cid: item.cid,
+ author: item.author,
+ record: item.record,
+ embed: item.embed,
+ repostedBy: None,
+ replyCount: item.replyCount,
+ upvoteCount: item.upvoteCount,
+ downvoteCount: 0,
+ repostCount: item.repostCount,
+ indexedAt: item.indexedAt,
+ viewer: None,
+ },
// don't want a loop here
parent: None,
- replyCount: item.replyCount,
// only going to depth of one here
replies: None,
- upvoteCount: item.upvoteCount,
- downvoteCount: 0,
- repostCount: item.repostCount,
- indexedAt: item.indexedAt,
- myState: None,
});
}
- let post = app_bsky::ThreadItem {
- uri: post_item.uri,
- cid: post_item.cid,
- author: post_item.author,
- record: post_item.record,
- embed: post_item.embed,
+ let pip = post_item.post;
+ let post = app_bsky::ThreadPostView {
+ post: app_bsky::PostView {
+ uri: pip.uri,
+ cid: pip.cid,
+ author: pip.author,
+ record: pip.record,
+ embed: pip.embed,
+ repostedBy: None,
+ replyCount: pip.replyCount,
+ upvoteCount: pip.upvoteCount,
+ downvoteCount: 0,
+ repostCount: pip.repostCount,
+ indexedAt: pip.indexedAt,
+ viewer: None,
+ },
parent,
- replyCount: post_item.replyCount,
replies: Some(children),
- upvoteCount: post_item.upvoteCount,
- downvoteCount: 0,
- repostCount: post_item.repostCount,
- indexedAt: post_item.indexedAt,
- myState: None,
};
Ok(app_bsky::PostThread { thread: post })
}
@@ -587,32 +608,29 @@ fn test_bsky_feeds() {
assert_eq!(alice_feed.feed.len(), 3);
assert_eq!(
- alice_feed.feed[2].uri,
+ alice_feed.feed[2].post.uri,
format!("at://{}/{}/{}", alice_did, post_nsid, alice_post1_tid)
);
// TODO: CID
- assert_eq!(alice_feed.feed[2].author.did, alice_did.to_string());
- assert_eq!(alice_feed.feed[2].author.handle, "alice.test");
- assert_eq!(alice_feed.feed[2].repostedBy, None);
- assert_eq!(
- alice_feed.feed[2].record["text"].as_str().unwrap(),
- "alice first post"
- );
- assert_eq!(alice_feed.feed[2].embed, None);
- assert_eq!(alice_feed.feed[2].replyCount, 0);
- assert_eq!(alice_feed.feed[2].repostCount, 0);
- assert_eq!(alice_feed.feed[2].upvoteCount, 1);
- assert_eq!(alice_feed.feed[2].downvoteCount, 0);
-
- assert_eq!(alice_feed.feed[1].author.did, alice_did.to_string());
- assert_eq!(alice_feed.feed[1].replyCount, 0);
- assert_eq!(alice_feed.feed[1].repostCount, 1);
- assert_eq!(alice_feed.feed[1].upvoteCount, 0);
-
- assert_eq!(alice_feed.feed[0].author.did, alice_did.to_string());
- assert_eq!(alice_feed.feed[0].replyCount, 1);
- assert_eq!(alice_feed.feed[0].repostCount, 0);
- assert_eq!(alice_feed.feed[0].upvoteCount, 0);
+ assert_eq!(alice_feed.feed[2].post.author.did, alice_did.to_string());
+ assert_eq!(alice_feed.feed[2].post.author.handle, "alice.test");
+ assert_eq!(alice_feed.feed[2].post.repostedBy, None);
+ assert_eq!(alice_feed.feed[2].post.record.text, "alice first post");
+ assert_eq!(alice_feed.feed[2].post.embed, None);
+ assert_eq!(alice_feed.feed[2].post.replyCount, 0);
+ assert_eq!(alice_feed.feed[2].post.repostCount, 0);
+ assert_eq!(alice_feed.feed[2].post.upvoteCount, 1);
+ assert_eq!(alice_feed.feed[2].post.downvoteCount, 0);
+
+ assert_eq!(alice_feed.feed[1].post.author.did, alice_did.to_string());
+ assert_eq!(alice_feed.feed[1].post.replyCount, 0);
+ assert_eq!(alice_feed.feed[1].post.repostCount, 1);
+ assert_eq!(alice_feed.feed[1].post.upvoteCount, 0);
+
+ assert_eq!(alice_feed.feed[0].post.author.did, alice_did.to_string());
+ assert_eq!(alice_feed.feed[0].post.replyCount, 1);
+ assert_eq!(alice_feed.feed[0].post.repostCount, 0);
+ assert_eq!(alice_feed.feed[0].post.upvoteCount, 0);
// test bob timeline: should include alice posts
let bob_timeline = bsky_get_timeline(&mut srv, &bob_did).unwrap();
@@ -622,12 +640,12 @@ fn test_bsky_feeds() {
}
assert_eq!(bob_timeline.feed.len(), 3);
assert_eq!(
- bob_timeline.feed[2].uri,
+ bob_timeline.feed[2].post.uri,
format!("at://{}/{}/{}", alice_did, post_nsid, alice_post1_tid)
);
// TODO: CID
- assert_eq!(bob_timeline.feed[2].author.did, alice_did.to_string());
- assert_eq!(bob_timeline.feed[2].author.handle, "alice.test");
+ assert_eq!(bob_timeline.feed[2].post.author.did, alice_did.to_string());
+ assert_eq!(bob_timeline.feed[2].post.author.handle, "alice.test");
// test bob feed: should include repost and reply
let bob_feed = bsky_get_author_feed(&mut srv, &bob_did).unwrap();
@@ -644,8 +662,8 @@ fn test_bsky_feeds() {
// TODO: "is a repost" (check record?)
*/
- assert_eq!(bob_feed.feed[0].author.did, bob_did.to_string());
- assert_eq!(bob_feed.feed[0].author.handle, "bob.test");
+ assert_eq!(bob_feed.feed[0].post.author.did, bob_did.to_string());
+ assert_eq!(bob_feed.feed[0].post.author.handle, "bob.test");
// test carol timeline: should include bob's repost and reply
let carol_timeline = bsky_get_timeline(&mut srv, &carol_did).unwrap();
@@ -734,15 +752,15 @@ fn test_bsky_thread() {
let post = bsky_get_thread(&mut srv, &AtUri::from_str(&bob_post1_uri).unwrap(), None)
.unwrap()
.thread;
- assert_eq!(post.author.did, bob_did.to_string());
- assert_eq!(post.author.handle, "bob.test".to_string());
- assert_eq!(post.embed, None);
- assert_eq!(post.replyCount, 1);
- assert_eq!(post.repostCount, 0);
- assert_eq!(post.upvoteCount, 0);
+ assert_eq!(post.post.author.did, bob_did.to_string());
+ assert_eq!(post.post.author.handle, "bob.test".to_string());
+ assert_eq!(post.post.embed, None);
+ assert_eq!(post.post.replyCount, 1);
+ assert_eq!(post.post.repostCount, 0);
+ assert_eq!(post.post.upvoteCount, 0);
assert_eq!(post.replies.as_ref().unwrap().len(), 1);
let post_replies = post.replies.unwrap();
- assert_eq!(post_replies[0].author.did, alice_did.to_string());
+ assert_eq!(post_replies[0].post.author.did, alice_did.to_string());
// TODO: root URI, etc
}
diff --git a/adenosine-pds/src/web.rs b/adenosine-pds/src/web.rs
index 8f15a49..fc75a93 100644
--- a/adenosine-pds/src/web.rs
+++ b/adenosine-pds/src/web.rs
@@ -29,8 +29,8 @@ pub struct AboutView {
pub struct AccountView {
pub domain: String,
pub did: Did,
- pub profile: app_bsky::Profile,
- pub feed: Vec<app_bsky::FeedItem>,
+ pub profile: app_bsky::ProfileView,
+ pub feed: Vec<app_bsky::FeedPostView>,
}
#[derive(Template)]
@@ -40,7 +40,7 @@ pub struct ThreadView {
pub did: Did,
pub collection: Nsid,
pub tid: Tid,
- pub post: app_bsky::ThreadItem,
+ pub post: app_bsky::ThreadPostView,
}
#[derive(Template)]
diff --git a/adenosine-pds/templates/account.html b/adenosine-pds/templates/account.html
index b19621d..f45b9ef 100644
--- a/adenosine-pds/templates/account.html
+++ b/adenosine-pds/templates/account.html
@@ -32,7 +32,7 @@
{% endif %}
{% for item in feed %}
- {% call macro::feed_item(item) %}
+ {% call macro::feed_item(item.post) %}
{% endfor %}
diff --git a/adenosine-pds/templates/macro.html b/adenosine-pds/templates/macro.html
index 7c61230..795400b 100644
--- a/adenosine-pds/templates/macro.html
+++ b/adenosine-pds/templates/macro.html
@@ -11,8 +11,8 @@
<div style="float: right;">
<a class="item_timestamp" href="/u/{{ item.author.handle }}/post/{{ item.uri|aturi_to_tid }}">
- {% if item.record["createdAt"].as_str().is_some() %}
- {{ item.record["createdAt"].as_str().unwrap() }}
+ {% if item.record.createdAt.is_some() %}
+ {{ item.record.createdAt.as_ref().unwrap() }}
{% else %}
{{ item.indexedAt }}
{% endif %}
@@ -25,16 +25,16 @@
{% endif %}
<a href="/u/{{ item.author.handle }}"><span class="handle">@{{ item.author.handle }}</span></a>
<br>
-{{ item.record["text"].as_str().unwrap() }}
+{{ item.record.text }}
<br>
<span class="counts">
[<a href="#">{{ item.upvoteCount }} upvote</a> / <a href="#">{{ item.repostCount }} repost</a> / <a href="#">{{ item.replyCount }} reply</a>]
<a href="{{ item.uri|aturi_to_path }}" class="pink">[inspect]</a>
</span>
-{% if item.record.get("reply").is_some() %}
+{% if item.record.reply.is_some() %}
<br>
-<b style="color: orange;">reply to:</b> <a href="{{ item.record["reply"]["uri"].as_str().unwrap()|aturi_to_thread_path }}">{{ item.record["reply"]["uri"] }}</a>
+<b style="color: orange;">reply to:</b> <a href="{{ item.record.reply.as_ref().unwrap().parent.uri|aturi_to_thread_path }}">{{ item.record.reply.as_ref().unwrap().parent.uri }}</a>
{% endif %}
<!-- TODO: "reposted by" -->
diff --git a/adenosine-pds/templates/thread.html b/adenosine-pds/templates/thread.html
index 608c971..c0a8684 100644
--- a/adenosine-pds/templates/thread.html
+++ b/adenosine-pds/templates/thread.html
@@ -4,16 +4,16 @@
{% block main %}
{% if post.parent.is_some() %}
- {% call macro::feed_item(post.parent.as_ref().unwrap()) %}
+ {% call macro::feed_item(post.parent.as_ref().unwrap().post) %}
<center><i>---</i></center>
{% endif %}
-{% call macro::feed_item(post) %}
+{% call macro::feed_item(post.post) %}
{% if post.replies.is_some() && post.replies.as_ref().unwrap().len() > 0 %}
<center><i>--- replies ---</i></center>
{% for item in post.replies.as_ref().unwrap() %}
- {% call macro::feed_item(item) %}
+ {% call macro::feed_item(item.post) %}
{% endfor %}
{% endif %}
diff --git a/adenosine/src/app_bsky/mod.rs b/adenosine/src/app_bsky/mod.rs
index 18a0449..d828bcb 100644
--- a/adenosine/src/app_bsky/mod.rs
+++ b/adenosine/src/app_bsky/mod.rs
@@ -8,6 +8,12 @@ pub struct Subject {
pub cid: Option<String>,
}
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct StrongRef {
+ pub uri: String,
+ pub cid: String,
+}
+
/// Generic over Re-post and Like
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
@@ -55,7 +61,7 @@ pub struct DeclRef {
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
-pub struct Profile {
+pub struct ProfileView {
pub did: String,
pub declaration: DeclRef,
pub handle: String,
@@ -67,13 +73,14 @@ pub struct Profile {
pub followsCount: u64,
pub membersCount: u64,
pub postsCount: u64,
- pub myState: serde_json::Value,
+ pub viewer: serde_json::Value,
}
+/// for Timeline or AuthorFeed
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
pub struct GenericFeed {
- pub feed: Vec<FeedItem>,
+ pub feed: Vec<FeedPostView>,
}
#[allow(non_snake_case)]
@@ -86,20 +93,48 @@ pub struct User {
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
-pub struct FeedItem {
+pub struct UserView {
+ pub did: String,
+ pub handle: String,
+ pub declaration: DeclRef,
+ pub displayName: Option<String>,
+ pub avatar: Option<String>,
+ pub viewer: Option<Value>,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct PostView {
pub uri: String,
pub cid: String,
- pub author: User,
- pub repostedBy: Option<User>,
- pub record: Value,
- //pub embed?: RecordEmbed | ExternalEmbed | UnknownEmbed,
- pub embed: Option<Value>,
+ pub author: UserView,
+ pub repostedBy: Option<UserView>,
+ pub record: Post,
+ pub embed: Option<PostEmbedView>,
pub replyCount: u64,
pub repostCount: u64,
pub upvoteCount: u64,
pub downvoteCount: u64,
pub indexedAt: String,
- pub myState: Option<Value>,
+ pub viewer: Option<Value>,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct ThreadPostView {
+ pub post: PostView,
+ // TODO: 'parent' and 'replies' should allow "NotFoundPost" for references that point to an
+ // unknown URI
+ pub parent: Option<Box<ThreadPostView>>,
+ pub replies: Option<Vec<ThreadPostView>>,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct FeedPostView {
+ pub post: PostView,
+ pub reply: Option<PostReply>,
+ pub reason: Option<Value>,
}
#[allow(non_snake_case)]
@@ -107,41 +142,92 @@ pub struct FeedItem {
pub struct Post {
pub text: String,
pub reply: Option<PostReply>,
+ pub entities: Option<Vec<PostEntity>>,
+ pub embed: Option<PostEmbed>,
pub createdAt: Option<String>,
}
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
pub struct PostReply {
+ // TODO: these should be StrongRef
pub parent: Subject,
pub root: Subject,
}
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
-pub struct PostThread {
- pub thread: ThreadItem,
+pub struct PostEntity {
+ pub index: TextSlice,
+ pub r#type: String,
+ pub value: String,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct TextSlice {
+ pub start: u64,
+ pub end: u64,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct PostEmbed {
+ pub external: Option<EmbedExternal>,
+ pub images: Option<Vec<EmbedImage>>,
}
-// TODO: 'parent' and 'replies' should allow "NotFoundPost" for references that point to an unknown
-// URI
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
-pub struct ThreadItem {
+pub struct PostEmbedView {
+ pub external: Option<EmbedExternalView>,
+ pub images: Option<Vec<EmbedImageView>>,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct EmbedExternal {
+ pub uri: String,
+ pub title: String,
+ pub description: String,
+ pub thumb: Option<Blob>,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct EmbedExternalView {
pub uri: String,
+ pub title: String,
+ pub description: String,
+ pub thumb: Option<String>,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct EmbedImage {
+ pub image: Blob,
+ pub alt: String,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct Blob {
pub cid: String,
- pub author: User,
- pub record: Value,
- //pub embed?: RecordEmbed | ExternalEmbed | UnknownEmbed,
- pub embed: Option<Value>,
- pub parent: Option<Box<ThreadItem>>,
- pub replyCount: u64,
- pub replies: Option<Vec<ThreadItem>>,
- pub repostCount: u64,
- pub upvoteCount: u64,
- pub downvoteCount: u64,
- pub indexedAt: String,
- pub myState: Option<Value>,
+ pub mimeType: String,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct EmbedImageView {
+ pub thumb: String,
+ pub fullsize: String,
+ pub alt: String,
+}
+
+#[allow(non_snake_case)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
+pub struct PostThread {
+ pub thread: ThreadPostView,
}
#[allow(non_snake_case)]