diff --git a/src/server/web/endpoint.rs b/src/server/web/endpoint.rs
index 19baadb..88cd309 100644
--- a/src/server/web/endpoint.rs
+++ b/src/server/web/endpoint.rs
@@ -5,7 +5,7 @@ use maud::{html, Markup, PreEscaped, DOCTYPE};
use url::Url;
use crate::{
- feed::{Feed, PostPreview},
+ feed::{Feed, Post, PostPreview},
server::{endpoint::EndpointService, web::sprite, EndpointParam},
source::{FromScratch, Source},
};
@@ -115,7 +115,7 @@ fn source_control_fragment(
div title="Source" .source { (url) }
}),
Some(Source::Templated(templated)) => Some(html! {
- div .template-container {
+ div .source-template-container {
@let queries = param.as_ref().ok().map(|p| p.extra_queries());
(source_template_fragment(templated, path, queries));
}
@@ -269,7 +269,7 @@ async fn fetch_and_render_feed(
) -> Markup {
html! {
@match endpoint.run(params).await {
- Ok(feed) => (render_feed(&feed)),
+ Ok(feed) => (render_feed(feed)),
Err(e) => {
div .flash.danger {
header { b { "Failed to fetch feed" } }
@@ -280,8 +280,11 @@ async fn fetch_and_render_feed(
}
}
-fn render_post(post: PostPreview) -> Markup {
- let link_url = Url::parse(&post.link).ok();
+fn render_post(preview: PostPreview, post: Post) -> Markup {
+ let link_url = Url::parse(&preview.link).ok();
+ let json =
+ serde_json::to_string_pretty(&post).unwrap_or_else(|e| e.to_string());
+ let id = format!("entry-{}", rand_id());
html! {
article data-display-mode="rendered" data-folded="true" .post-entry {
@@ -289,16 +292,20 @@ fn render_post(post: PostPreview) -> Markup {
span .icon-container.fold-icon onclick="toggleFold(this)" title="Expand" {
(sprite("caret-right"))
}
- span .icon-container.raw-icon onclick="toggleRaw(this)" title="Raw HTML body" {
- (sprite("source-code"))
+ span .icon-container.raw-icon onclick="toggleRaw(this)" title="HTML body" {
+ (sprite("code"))
+ }
+ span .icon-container.json-icon onclick="toggleJson(this)" title="JSON structure" {
+ (sprite("json"))
}
- div .row.flex.grow style="margin-left: .5rem" { (post.title); (external_link(&post.link)) }
+ div .row.flex.grow style="margin-left: .5rem" {
+ (preview.title); (external_link(&preview.link))
+ }
}
- @if let Some(body) = &post.body {
- section {
- @let id = format!("entry-{}", rand_id());
+ section {
+ @if let Some(body) = &preview.body {
@let content = santize_html(body, link_url);
div id=(id) .entry-content.rendered {
template {
@@ -310,16 +317,25 @@ fn render_post(post: PostPreview) -> Markup {
div .entry-content.raw {
pre { code .language-html { (body) } }
}
+ } @else {
+ div id=(id) .entry-content.rendered {
+ "No body"
+ }
+ div .entry-content.raw {
+ pre { code .language-html { } }
+ }
+ }
+
+ div .entry-content.json {
+ pre { code .language-json { (json) } }
}
- } @else {
- section { "No body" }
}
footer {
- @if let Some(date) = post.date {
+ @if let Some(date) = preview.date {
time .inline datetime=(date.to_rfc3339()) { (date.to_rfc2822()) }
}
- @if let Some(author) = &post.author {
+ @if let Some(author) = &preview.author {
span .ml-1 {
(PreEscaped("By "));
address .inline rel="author" { (author) }
@@ -330,8 +346,9 @@ fn render_post(post: PostPreview) -> Markup {
}
}
-fn render_feed(feed: &Feed) -> Markup {
+fn render_feed(mut feed: Feed) -> Markup {
let preview = feed.preview();
+ let posts = feed.take_posts();
html! {
h3 style="display:flex" {
@@ -343,8 +360,8 @@ fn render_feed(feed: &Feed) -> Markup {
}
p { (format!("Entries ({}):", preview.posts.len())) }
- @for post in preview.posts {
- (render_post(post))
+ @for (preview, post) in preview.posts.into_iter().zip(posts) {
+ (render_post(preview, post))
}
}
}
@@ -362,6 +379,7 @@ fn inline_styles() -> &'static str {
.icon-container {
display: inline-flex;
align-self: center;
+ margin: 0 0.2rem;
}
&[data-folded="false"] {
@@ -398,6 +416,14 @@ fn inline_styles() -> &'static str {
display: block;
}
}
+ &[data-display-mode="json"] {
+ .json-icon > .icon {
+ color: var(--active);
+ }
+ .entry-content.json {
+ display: block;
+ }
+ }
}
.source {
@@ -531,10 +557,18 @@ fn inline_script() -> &'static str {
function toggleRaw(element) {
const article = element.closest("article");
+ article.dataset.folded = "false";
article.dataset.displayMode =
article.dataset.displayMode === "rendered" ? "raw" : "rendered";
}
+ function toggleJson(element) {
+ const article = element.closest("article");
+ article.dataset.folded = "false";
+ article.dataset.displayMode =
+ article.dataset.displayMode === "rendered" ? "json" : "rendered";
+ }
+
function gatherFilterSkip() {
const skipped = [...document.querySelectorAll(".filter-item > var")].filter(x=>!+x.dataset.enabled).map(x => x.dataset.index).join(",");
if (skipped === "") {
diff --git a/static/sprite.svg b/static/sprite.svg
index 90e4c16..7aea624 100644
--- a/static/sprite.svg
+++ b/static/sprite.svg
@@ -28,11 +28,18 @@ Generated using https://svgsprit.es/
-->
diff --git a/static/svg/json.svg b/static/svg/json.svg
new file mode 100644
index 0000000..8be4fe9
--- /dev/null
+++ b/static/svg/json.svg
@@ -0,0 +1 @@
+
\ No newline at end of file