aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2022-11-10 16:32:36 -0800
committerBryan Newbold <bnewbold@robocracy.org>2022-11-10 16:32:36 -0800
commitcd67cbbb2827c161aa6e99c93fe57f5500cbb789 (patch)
tree74288ae72c0f7ad07cf1f77416b180ab9a4f65c0
parent5e149eef22d34e5a2b2081de3533dee3373d47f8 (diff)
downloadadenosine-cd67cbbb2827c161aa6e99c93fe57f5500cbb789.tar.gz
adenosine-cd67cbbb2827c161aa6e99c93fe57f5500cbb789.zip
pds: more web view implementation
-rw-r--r--adenosine-pds/Cargo.toml2
-rw-r--r--adenosine-pds/src/bsky.rs2
-rw-r--r--adenosine-pds/src/lib.rs104
-rw-r--r--adenosine-pds/src/models.rs2
-rw-r--r--adenosine-pds/src/repo.rs2
-rw-r--r--adenosine-pds/src/web.rs59
-rw-r--r--adenosine-pds/templates/account.html36
-rw-r--r--adenosine-pds/templates/adenosine.css74
-rw-r--r--adenosine-pds/templates/at_collection.html7
-rw-r--r--adenosine-pds/templates/at_record.html7
-rw-r--r--adenosine-pds/templates/at_repo.html16
-rw-r--r--adenosine-pds/templates/base.html10
-rw-r--r--adenosine-pds/templates/macro.html41
-rw-r--r--adenosine-pds/templates/post.html5
-rw-r--r--adenosine-pds/templates/profile.html7
-rw-r--r--adenosine-pds/templates/thread.html8
16 files changed, 276 insertions, 106 deletions
diff --git a/adenosine-pds/Cargo.toml b/adenosine-pds/Cargo.toml
index 1637164..7118956 100644
--- a/adenosine-pds/Cargo.toml
+++ b/adenosine-pds/Cargo.toml
@@ -43,7 +43,7 @@ ucan = "0.7.0-alpha.1"
bs58 = "*"
async-trait = "*"
dotenv = "*"
-askama = "0.11"
+askama = { version = "0.11", features = ["serde-json"] }
time = { version = "*", features = ["formatting"] }
# for vendored iroh-car
diff --git a/adenosine-pds/src/bsky.rs b/adenosine-pds/src/bsky.rs
index 66b05eb..a4d025b 100644
--- a/adenosine-pds/src/bsky.rs
+++ b/adenosine-pds/src/bsky.rs
@@ -248,7 +248,7 @@ pub fn bsky_get_thread(
_srv: &mut AtpService,
_uri: &AtUri,
_depth: Option<u64>,
-) -> Result<GenericFeed> {
+) -> Result<PostThread> {
// TODO: what is the best way to implement this? recurisvely? just first-level children to
// start?
unimplemented!()
diff --git a/adenosine-pds/src/lib.rs b/adenosine-pds/src/lib.rs
index 7b517f2..894f4a2 100644
--- a/adenosine-pds/src/lib.rs
+++ b/adenosine-pds/src/lib.rs
@@ -172,10 +172,10 @@ impl AtpService {
(GET) ["/"] => {
let host = request.header("Host").unwrap_or("localhost");
if Some(host.to_string()) == config.registration_domain {
+ web_wrap(account_view_handler(&srv, &host, request))
+ } else {
let view = GenericHomeView { domain: host.to_string() };
Response::html(view.render().unwrap())
- } else {
- web_wrap(home_profile_handler(&srv, request))
}
},
(GET) ["/about"] => {
@@ -184,19 +184,19 @@ impl AtpService {
Response::html(view.render().unwrap())
},
(GET) ["/u/{handle}", handle: String] => {
- web_wrap(profile_handler(&srv, &handle, request))
+ web_wrap(account_view_handler(&srv, &handle, request))
},
- (GET) ["/u/{did}/{collection}/{tid}", did: Did, collection: Nsid, tid: Tid] => {
- web_wrap(post_handler(&srv, &did, &collection, &tid, request))
+ (GET) ["/u/{did}/post/{tid}", did: Did, tid: Tid] => {
+ web_wrap(thread_view_handler(&srv, &did, &tid, request))
},
(GET) ["/at/{did}", did: Did] => {
- web_wrap(repo_handler(&srv, &did, request))
+ web_wrap(repo_view_handler(&srv, &did, request))
},
(GET) ["/at/{did}/{collection}", did: Did, collection: Nsid] => {
- web_wrap(collection_handler(&srv, &did, &collection, request))
+ web_wrap(collection_view_handler(&srv, &did, &collection, request))
},
(GET) ["/at/{did}/{collection}/{tid}", did: Did, collection: Nsid, tid: Tid] => {
- web_wrap(record_handler(&srv, &did, &collection, &tid, request))
+ web_wrap(record_view_handler(&srv, &did, &collection, &tid, request))
},
// ============ Static Files (compiled in to executable)
(GET) ["/static/adenosine.css"] => {
@@ -660,77 +660,57 @@ fn xrpc_post_handler(
}
}
-fn home_profile_handler(srv: &Mutex<AtpService>, request: &Request) -> Result<String> {
- let host = request.header("Host").unwrap_or("localhost");
- // XXX
- let did = Did::from_str(host)?;
- let mut _srv = srv.lock().expect("service mutex");
-
- // TODO: get profile (bsky helper)
- // TODO: get feed (bsky helper)
-
- Ok(ProfileView {
- domain: host.to_string(),
- did: did,
- profile: json!({}),
- feed: vec![],
- }
- .render()?)
-}
-
// TODO: did, collection, tid have already been parsed by this point
-fn profile_handler(srv: &Mutex<AtpService>, did: &str, request: &Request) -> Result<String> {
+fn account_view_handler(
+ srv: &Mutex<AtpService>,
+ handle: &str,
+ request: &Request,
+) -> Result<String> {
let host = request.header("Host").unwrap_or("localhost");
- let did = Did::from_str(did)?;
- let mut _srv = srv.lock().expect("service mutex");
-
- // TODO: get profile (bsky helper)
- // TODO: get feed (bsky helper)
+ let mut srv = srv.lock().expect("service mutex");
+ // TODO: unwrap as 404
+ let did = srv
+ .atp_db
+ .resolve_handle(handle)?
+ .ok_or(XrpcError::NotFound(format!(
+ "no DID found for handle: {}",
+ handle
+ )))?;
- Ok(ProfileView {
+ Ok(AccountView {
domain: host.to_string(),
- did: did,
- profile: json!({}),
- feed: vec![],
+ did: did.clone(),
+ profile: bsky_get_profile(&mut srv, &did)?,
+ feed: bsky_get_author_feed(&mut srv, &did)?.feed,
}
.render()?)
}
-fn post_handler(
+fn thread_view_handler(
srv: &Mutex<AtpService>,
- did: &str,
- collection: &str,
- tid: &str,
+ handle: &str,
+ tid: &Tid,
request: &Request,
) -> Result<String> {
let host = request.header("Host").unwrap_or("localhost");
- let did = Did::from_str(did)?;
- let collection = Nsid::from_str(collection)?;
- let rkey = Tid::from_str(tid)?;
+ let collection = Nsid::from_str("app.bsky.feed.post")?;
let mut srv = srv.lock().expect("service mutex");
+ // TODO: not unwrap
+ let did = srv.atp_db.resolve_handle(handle)?.unwrap();
- let post = match srv.repo.get_atp_record(&did, &collection, &rkey) {
- // TODO: format as JSON, not text debug
- Ok(Some(ipld)) => ipld_into_json_value(ipld),
- Ok(None) => Err(anyhow!(XrpcError::NotFound(format!(
- "could not find record: /{}/{}",
- collection, rkey
- ))))?,
- Err(e) => Err(e)?,
- };
-
- Ok(PostView {
+ // TODO: could construct URI directly
+ let uri = AtUri::from_str(&format!("at://{}/{}/{}", did, collection, tid))?;
+ Ok(ThreadView {
domain: host.to_string(),
- did: did,
- collection: collection,
- tid: rkey,
- post_text: post["text"].as_str().unwrap().to_string(), // TODO: unwrap
- post_created_at: "some-time".to_string(),
+ did,
+ collection,
+ tid: tid.clone(),
+ thread: bsky_get_thread(&mut srv, &uri, None)?.thread,
}
.render()?)
}
-fn repo_handler(srv: &Mutex<AtpService>, did: &str, request: &Request) -> Result<String> {
+fn repo_view_handler(srv: &Mutex<AtpService>, did: &str, request: &Request) -> Result<String> {
let host = request.header("Host").unwrap_or("localhost");
let did = Did::from_str(did)?;
@@ -756,7 +736,7 @@ fn repo_handler(srv: &Mutex<AtpService>, did: &str, request: &Request) -> Result
.render()?)
}
-fn collection_handler(
+fn collection_view_handler(
srv: &Mutex<AtpService>,
did: &str,
collection: &str,
@@ -794,7 +774,7 @@ fn collection_handler(
.render()?)
}
-fn record_handler(
+fn record_view_handler(
srv: &Mutex<AtpService>,
did: &str,
collection: &str,
diff --git a/adenosine-pds/src/models.rs b/adenosine-pds/src/models.rs
index 0f47c2d..3af780e 100644
--- a/adenosine-pds/src/models.rs
+++ b/adenosine-pds/src/models.rs
@@ -155,7 +155,7 @@ pub struct PostReply {
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
pub struct PostThread {
- pub thread: ThreadItem,
+ pub thread: Vec<ThreadItem>,
}
#[allow(non_snake_case)]
diff --git a/adenosine-pds/src/repo.rs b/adenosine-pds/src/repo.rs
index 1b24be8..9f5fca0 100644
--- a/adenosine-pds/src/repo.rs
+++ b/adenosine-pds/src/repo.rs
@@ -17,7 +17,7 @@ use std::collections::HashSet;
use std::path::PathBuf;
use std::str::FromStr;
-#[derive(Debug)]
+#[derive(Debug, serde::Serialize)]
pub struct RepoCommit {
pub sig: Box<[u8]>,
pub commit_cid: Cid,
diff --git a/adenosine-pds/src/web.rs b/adenosine-pds/src/web.rs
index e783b5a..5b9d6de 100644
--- a/adenosine-pds/src/web.rs
+++ b/adenosine-pds/src/web.rs
@@ -17,23 +17,22 @@ pub struct AboutView {
}
#[derive(Template)]
-#[template(path = "profile.html")]
-pub struct ProfileView {
+#[template(path = "account.html")]
+pub struct AccountView {
pub domain: String,
pub did: Did,
- pub profile: serde_json::Value,
- pub feed: Vec<serde_json::Value>,
+ pub profile: Profile,
+ pub feed: Vec<FeedItem>,
}
#[derive(Template)]
-#[template(path = "post.html")]
-pub struct PostView {
+#[template(path = "thread.html")]
+pub struct ThreadView {
pub domain: String,
pub did: Did,
pub collection: Nsid,
pub tid: Tid,
- pub post_text: String,
- pub post_created_at: String,
+ pub thread: Vec<ThreadItem>,
}
#[derive(Template)]
@@ -63,3 +62,47 @@ pub struct RecordView {
pub tid: Tid,
pub record: serde_json::Value,
}
+
+mod filters {
+ use crate::AtUri;
+ use std::str::FromStr;
+
+ pub fn aturi_to_path(aturi: &str) -> ::askama::Result<String> {
+ let aturi = AtUri::from_str(aturi).expect("aturi parse");
+ if aturi.record.is_some() {
+ Ok(format!(
+ "/at/{}/{}/{}",
+ aturi.repository,
+ aturi.collection.unwrap(),
+ aturi.record.unwrap()
+ ))
+ } else if aturi.collection.is_some() {
+ Ok(format!(
+ "/at/{}/{}",
+ aturi.repository,
+ aturi.collection.unwrap()
+ ))
+ } else {
+ Ok(format!("/at/{}", aturi.repository))
+ }
+ }
+
+ pub fn aturi_to_thread_path(aturi: &str) -> ::askama::Result<String> {
+ let aturi = AtUri::from_str(aturi).expect("aturi parse");
+ Ok(format!(
+ "/u/{}/post/{}",
+ aturi.repository,
+ aturi.record.unwrap()
+ ))
+ }
+
+ pub fn aturi_to_tid(aturi: &str) -> ::askama::Result<String> {
+ let aturi = AtUri::from_str(aturi).expect("aturi parse");
+ if aturi.record.is_some() {
+ Ok(aturi.record.unwrap())
+ } else {
+ // TODO: raise an askama error here?
+ Ok("<MISSING>".to_string())
+ }
+ }
+}
diff --git a/adenosine-pds/templates/account.html b/adenosine-pds/templates/account.html
new file mode 100644
index 0000000..bd015d5
--- /dev/null
+++ b/adenosine-pds/templates/account.html
@@ -0,0 +1,36 @@
+{% extends "base.html" %}
+{% import "macro.html" as macro %}
+
+{% block main %}
+
+<article class="profile">
+<h4>
+ {% if profile.displayName.is_some() %}
+ <span class="display_name">{{ profile.displayName.as_ref().unwrap() }}</span>
+ {% endif %}
+ <span class="handle">@{{ profile.handle }}</span>
+</h4>
+<a href="/at/{{ profile.did }}" class="ident">{{ profile.did }}</a>
+
+{% if profile.description.is_some() %}
+ <p>{{ profile.description.as_ref().unwrap() }}
+{% endif %}
+
+<p class="counts">
+ [<a href="#">{{ profile.followersCount}} followers</a> /
+ <a href="#">{{ profile.followsCount }} follows</a>]
+</p>
+</article>
+
+{% if feed.len() == 0 %}
+ <center><i>--- no posts yet! ---</i></center>
+{% else %}
+ <center><i>--- showing {{ feed.len() }} of {{ profile.postsCount }} posts</i> ---</center>
+{% endif %}
+
+{% for item in feed %}
+ {% call macro::feed_item(item) %}
+{% endfor %}
+
+
+{% endblock %}
diff --git a/adenosine-pds/templates/adenosine.css b/adenosine-pds/templates/adenosine.css
index 0686f9e..9a0f890 100644
--- a/adenosine-pds/templates/adenosine.css
+++ b/adenosine-pds/templates/adenosine.css
@@ -622,5 +622,75 @@ progress:indeterminate::-moz-progress-bar {
}
/********** adenosine tweaks **********/
-body { font-family: var(--mono-font); }
-a { text-decoration: none; }
+body {
+ font-family: var(--mono-font);
+}
+a {
+ text-decoration: none;
+}
+main {
+ font-size: smaller;
+ padding-top: 0px;
+}
+h2 {
+ margin-top: 0px;
+ margin-bottom: 1rem;
+ font-size: 2.5em;
+}
+nav.header {
+ border-bottom: 2px dashed var(--border);
+}
+nav.header img {
+ vertical-align: middle;
+}
+nav.header span {
+ vertical-align: middle;
+ color: var(--text);
+}
+nav.header h1 {
+ font-weight: normal;
+ font-size: 2rem;
+ margin-top: 1rem;
+ margin-bottom: 0.5rem;
+}
+.ident, a:hover.ident, a:visited.ident {
+ color: var(--text-light);
+}
+.smaller {
+ font-size: smaller;
+}
+body footer {
+ border-top: 2px dashed var(--border);
+ padding: 1rem 1rem 1rem 1rem;
+}
+.counts {
+ color: var(--text-light);
+}
+.counts a {
+ color: var(--text-light);
+}
+p.counts {
+ margin-bottom: 0px;
+}
+.display_name {
+ color: green;
+ font-weight: normal;
+}
+.handle {
+ color: green;
+ font-weight: bold;
+}
+.feed_item {
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+}
+.profile h4 {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+.repo_aturi, .repo_aturi a, .repo_aturi a:visited {
+ color: var(--code);
+ font-size: smaller;
+ overflow: auto;
+ font-weight: bold;
+}
diff --git a/adenosine-pds/templates/at_collection.html b/adenosine-pds/templates/at_collection.html
index b4b7036..670cb0d 100644
--- a/adenosine-pds/templates/at_collection.html
+++ b/adenosine-pds/templates/at_collection.html
@@ -1,12 +1,13 @@
{% extends "base.html" %}
{% block main %}
-<p><b>at://{{ did }}/{{ collection }}/</b>
+<h2>ATP Repository Explorer</h2>
+<div class="repo_aturi">at://<a href="/at/{{ did }}">{{ did }}</a>/{{ collection }}/</div>
-<p><b>records:</b>
+<h4>Records</h4>
<ul>
{% for record in records %}
- <li><a href="/at/{{did}}/{{collection}}/{{record["tid"].as_str().unwrap() }}">{{ record["tid"].as_str().unwrap() }}</a>
+ <li>/{{ collection }}/<a href="/at/{{did}}/{{collection}}/{{record["tid"].as_str().unwrap() }}">{{ record["tid"].as_str().unwrap() }}</a>/
{% endfor %}
</ul>
{% endblock %}
diff --git a/adenosine-pds/templates/at_record.html b/adenosine-pds/templates/at_record.html
index 10ed838..829ed4a 100644
--- a/adenosine-pds/templates/at_record.html
+++ b/adenosine-pds/templates/at_record.html
@@ -1,8 +1,9 @@
{% extends "base.html" %}
{% block main %}
-<p><b>at://{{ did }}/{{ collection }}/{{ tid }}</b>
+<h2>ATP Repository Explorer</h2>
+<div class="repo_aturi">at://<a href="/at/{{ did }}">{{ did }}</a>/<a href="/at/{{ did }}/{{ collection }}">{{ collection }}</a>/{{ tid }}</div>
-<p><b>record json:</b>
-<pre><code>{{ record }}</code></pre>
+<h4>JSON</h4>
+<pre><code>{{ record|json }}</code></pre>
{% endblock %}
diff --git a/adenosine-pds/templates/at_repo.html b/adenosine-pds/templates/at_repo.html
index dcf4d87..384b8dc 100644
--- a/adenosine-pds/templates/at_repo.html
+++ b/adenosine-pds/templates/at_repo.html
@@ -1,18 +1,20 @@
{% extends "base.html" %}
{% block main %}
-<p><b>at://{{ did }}/</b>
+<h2>ATP Repository Explorer</h2>
+<div class="repo_aturi">at://{{ did }}/</div>
-<p><b>collections:</b>
+<h4>Collections</h4>
<ul>
{% for collection in describe.collections %}
- <li><a href="/at/{{ did }}/{{ collection }}">{{collection}}/</a>
+ <li>/<a href="/at/{{ did }}/{{ collection }}">{{collection}}</a>/
{% endfor %}
</ul>
-<p>repo commit:
-<pre><code>{{ "{:?}"|format(commit) }}</code></pre>
+<h4>Describe</h4>
+<pre><code>{{ describe|json }}</code></pre>
+
+<h4>Commit Node</h4>
+<pre><code>{{ commit|json }}</code></pre>
-<p>repo describe
-<pre><code>{{ "{:?}"|format(describe) }}</code></pre>
{% endblock %}
diff --git a/adenosine-pds/templates/base.html b/adenosine-pds/templates/base.html
index 0ae2061..cb88c0c 100644
--- a/adenosine-pds/templates/base.html
+++ b/adenosine-pds/templates/base.html
@@ -9,11 +9,11 @@
{% block head %}{% endblock %}
</head>
<body>
- <nav>
+ <nav class="header">
<a href="/">
- <h3>
- <img src="/static/logo_128.png" width="64" height="64">{{ domain }}
- </h3>
+ <h1>
+ <img src="/static/logo_128.png" width="48" height="48"><span> {{ domain }}</span>
+ </h1>
</a>
</nav>
<hr>
@@ -25,7 +25,7 @@
<nav>
<a href="/">home</a> -
<a href="/about">about</a> -
- <a href="https://gitlab.com/bnewbold/adenosine">source code</a>
+ <a href="https://gitlab.com/bnewbold/adenosine">adenosine v{{ env!("CARGO_PKG_VERSION") }}</a>
</nav>
</footer>
</body>
diff --git a/adenosine-pds/templates/macro.html b/adenosine-pds/templates/macro.html
new file mode 100644
index 0000000..1d38482
--- /dev/null
+++ b/adenosine-pds/templates/macro.html
@@ -0,0 +1,41 @@
+
+{% macro feed_item(item) %}
+
+<div class="feed_item">
+{% if item.repostedBy.is_some() %}
+ {% if item.author.displayName.is_some() %}{{ item.author.displayName.as_ref().unwrap() }}{% endif %}
+ <b>@{{ item.author.handle }}</b>
+{% endif %}
+
+<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() }}
+ {% else %}
+ {{ item.indexedAt }}
+ {% endif %}
+ </a>
+</div>
+
+
+{% if item.author.displayName.is_some() %}
+ <a href="/u/{{ item.author.handle }}"><span class="display_name">{{ item.author.displayName.as_ref().unwrap() }}</span></a>
+{% endif %}
+<a href="/u/{{ item.author.handle }}"><span class="handle">@{{ item.author.handle }}</span></a>
+<br>
+{{ item.record["text"].as_str().unwrap() }}
+<br>
+<span class="counts">
+ [<a href="#">{{ item.likeCount }} like</a> / <a href="#">{{ item.repostCount }} repost</a> / <a href="#">{{ item.replyCount }} reply</a>]
+</span>
+
+{% if item.record.get("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>
+{% endif %}
+
+<!-- TODO: "reposted by" -->
+<!-- TODO: "reply to" -->
+
+</div>
+{% endmacro %}
diff --git a/adenosine-pds/templates/post.html b/adenosine-pds/templates/post.html
deleted file mode 100644
index d7e6c85..0000000
--- a/adenosine-pds/templates/post.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "base.html" %}
-
-{% block post %}
-Post page (TODO)
-{% endblock %}
diff --git a/adenosine-pds/templates/profile.html b/adenosine-pds/templates/profile.html
deleted file mode 100644
index 7c17951..0000000
--- a/adenosine-pds/templates/profile.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "base.html" %}
-
-{% block main %}
-<p><b>at://{{ did }}/</b>
-
-<p>Profile page (TODO)
-{% endblock %}
diff --git a/adenosine-pds/templates/thread.html b/adenosine-pds/templates/thread.html
new file mode 100644
index 0000000..e2e2e96
--- /dev/null
+++ b/adenosine-pds/templates/thread.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+{% import "macro.html" as macro %}
+
+{% block post %}
+
+Post stuff will go here
+
+{% endblock %}