split ui into smaller templates and separate css
This commit is contained in:
parent
56b5609da3
commit
801934e5aa
|
@ -1,7 +1,9 @@
|
|||
use async_std::channel::Sender;
|
||||
use log::{debug, warn};
|
||||
use markdown;
|
||||
use rocket::{form::Form, get, post, response::Redirect, uri, FromForm, State};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use uri_encode;
|
||||
|
||||
use crate::{db::Database, sbot, task_loop::Task, utils, WhoAmI};
|
||||
|
||||
|
@ -17,7 +19,7 @@ pub async fn home(db: &State<Database>) -> Template {
|
|||
let mut context = Context::new();
|
||||
context.insert("peers", &peers);
|
||||
|
||||
Template::render("home", &context.into_json())
|
||||
Template::render("base", &context.into_json())
|
||||
}
|
||||
|
||||
#[get("/posts/<public_key>")]
|
||||
|
@ -30,7 +32,7 @@ pub async fn posts(db: &State<Database>, public_key: &str) -> Template {
|
|||
context.insert("peers", &peers);
|
||||
context.insert("posts", &posts);
|
||||
|
||||
Template::render("home", &context.into_json())
|
||||
Template::render("base", &context.into_json())
|
||||
}
|
||||
|
||||
#[get("/posts/<public_key>/<msg_id>")]
|
||||
|
@ -41,12 +43,54 @@ pub async fn post(db: &State<Database>, public_key: &str, msg_id: &str) -> Templ
|
|||
|
||||
let mut context = Context::new();
|
||||
context.insert("selected_peer", &public_key);
|
||||
context.insert(
|
||||
"selected_peer_encoded",
|
||||
&uri_encode::encode_uri_component(public_key),
|
||||
);
|
||||
context.insert("selected_post", &msg_id);
|
||||
context.insert(
|
||||
"selected_post_encoded",
|
||||
&uri_encode::encode_uri_component(msg_id),
|
||||
);
|
||||
context.insert("peers", &peers);
|
||||
context.insert("posts", &posts);
|
||||
// TODO: consider converting markdown to html here
|
||||
context.insert("post", &post);
|
||||
context.insert("post_is_selected", &true);
|
||||
|
||||
Template::render("home", &context.into_json())
|
||||
Template::render("base", &context.into_json())
|
||||
}
|
||||
|
||||
#[get("/posts/<public_key>/<msg_id>/read")]
|
||||
pub async fn mark_post_read(db: &State<Database>, public_key: &str, msg_id: &str) -> Redirect {
|
||||
// Retrieve the post from the database, mark it as read and reinsert it.
|
||||
if let Ok(Some(mut post)) = db.get_post(public_key, msg_id) {
|
||||
post.read = true;
|
||||
db.add_post(public_key, post).unwrap();
|
||||
} else {
|
||||
warn!(
|
||||
"failed to find post {} authored by {} in database",
|
||||
msg_id, public_key
|
||||
)
|
||||
}
|
||||
|
||||
Redirect::to(uri!(post(public_key, msg_id)))
|
||||
}
|
||||
|
||||
#[get("/posts/<public_key>/<msg_id>/unread")]
|
||||
pub async fn mark_post_unread(db: &State<Database>, public_key: &str, msg_id: &str) -> Redirect {
|
||||
// Retrieve the post from the database, mark it as unread and reinsert it.
|
||||
if let Ok(Some(mut post)) = db.get_post(public_key, msg_id) {
|
||||
post.read = false;
|
||||
db.add_post(public_key, post).unwrap();
|
||||
} else {
|
||||
warn!(
|
||||
"failed to find post {} authored by {} in database",
|
||||
msg_id, public_key
|
||||
)
|
||||
}
|
||||
|
||||
Redirect::to(uri!(post(public_key, msg_id)))
|
||||
}
|
||||
|
||||
#[post("/subscribe", data = "<peer>")]
|
||||
|
@ -77,6 +121,8 @@ pub async fn subscribe_form(
|
|||
_ => (),
|
||||
}
|
||||
let peer = peer.public_key.to_string();
|
||||
// Fetch all root posts authored by the peer we're subscribing
|
||||
// to. Posts will be added to the key-value database.
|
||||
if let Err(e) = tx.send(Task::FetchAll(peer)).await {
|
||||
warn!("task loop error: {}", e)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
.nav {
|
||||
background-color: lightgreen;
|
||||
border: 5px solid #19A974;
|
||||
border-radius: 15px;
|
||||
grid-area: nav;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.peers {
|
||||
background-color: lightblue;
|
||||
border: 5px solid #357EDD;
|
||||
border-radius: 15px;
|
||||
grid-area: peers;
|
||||
}
|
||||
|
||||
.posts {
|
||||
background-color: bisque;
|
||||
border: 5px solid #FF6300;
|
||||
border-radius: 15px;
|
||||
grid-area: posts;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.post > ul {
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: lightyellow;
|
||||
border: 5px solid #FFD700;
|
||||
border-radius: 15px;
|
||||
grid-area: content;
|
||||
padding: 1.5rem;
|
||||
overflow-y: scroll;
|
||||
word-wrap: anywhere;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
.flex-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
*/
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: 1fr 3fr 4fr;
|
||||
grid-template-areas:
|
||||
'nav nav nav nav nav'
|
||||
'peers posts posts posts posts'
|
||||
'peers content content content content';
|
||||
grid-gap: 10px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 5px;
|
||||
overflow: hidden;
|
||||
height: 85vh;
|
||||
}
|
||||
|
||||
.grid-container > div {
|
||||
/* background-color: rgba(255, 255, 255, 0.8); */
|
||||
/* text-align: center; */
|
||||
/* padding: 20px 0; */
|
||||
/* font-size: 30px; */
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-container > input {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
a { text-decoration: none; color: black; }
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>lykin</title>
|
||||
<meta name="description" content="lykin: an SSB tutorial application">
|
||||
<meta name="author" content="glyph">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/css/lykin.css">
|
||||
</head>
|
||||
<body class="container">
|
||||
<a href="/"><h1 style="margin-left: 15px;">lykin</h1></a>
|
||||
<div class="grid-container">
|
||||
{% include "topbar" %}
|
||||
{% include "peer_list" %}
|
||||
{% include "post_list" %}
|
||||
{% include "post_content" %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,133 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>lykin</title>
|
||||
<meta name="description" content="lykin: an SSB tutorial application">
|
||||
<meta name="author" content="glyph">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<style>
|
||||
.nav {
|
||||
grid-area: nav;
|
||||
border: 5px solid #19A974;
|
||||
border-radius: 15px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.peers {
|
||||
grid-area: peers;
|
||||
border: 5px solid #357EDD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.posts {
|
||||
grid-area: posts;
|
||||
border: 5px solid #FF6300;
|
||||
border-radius: 15px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.content {
|
||||
border: 5px solid #FFD700;
|
||||
border-radius: 15px;
|
||||
grid-area: content;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: 1fr 200px 4fr;
|
||||
grid-template-areas:
|
||||
'nav nav nav nav nav'
|
||||
'peers posts posts posts posts'
|
||||
'peers content content content content';
|
||||
grid-gap: 10px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.grid-container > div {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
/* text-align: center; */
|
||||
/* padding: 20px 0; */
|
||||
/* font-size: 30px; */
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-container > input {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
a { text-decoration: none; color: black; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="container">
|
||||
<a href="/"><h1 style="margin-left: 15px;">lykin</h1></a>
|
||||
<div class="grid-container">
|
||||
<div class="nav">
|
||||
<div class="flex-container">
|
||||
<a href="/"><img src="/icons/download.png" style="width: 55px;"></a>
|
||||
<a href="/" style="margin-left: 20px;"><img src="/icons/read_post.png" style="width: 55px;"></a>
|
||||
<a href="/" style="margin-left: 20px;"><img src="/icons/unread_post.png" style="width: 55px;"></a>
|
||||
<a href="/" style="margin-left: 20px;"><img src="/icons/delete_post.png" style="width: 55px;"></a>
|
||||
<form class="flex-container" style="margin-left: auto; margin-right: 10px;" action="/subscribe" method="post">
|
||||
<label for="public_key">Public Key</label>
|
||||
<input type="text" id="public_key" name="public_key" size=50 maxlength=53>
|
||||
<input type="submit" value="Subscribe">
|
||||
<input type="submit" value="Unsubscribe" formaction="/unsubscribe">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="peers" style="text-align: center;">
|
||||
<ul style="padding-left: 0;">
|
||||
{% for peer in peers -%}
|
||||
<li style="list-style: none; font-size: 12px;">
|
||||
<a href="/posts/{{ peer | replace(from="/", to="%2F") }}">
|
||||
<code style="word-wrap: anywhere;{% if selected_peer and peer == selected_peer %} font-weight: bold;{% endif %}">{{ peer }}</code>
|
||||
</a>
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="posts">
|
||||
{% if posts %}
|
||||
<ul style="padding-left: 25px;">
|
||||
{% for post in posts -%}
|
||||
<li class="flex" style="list-style: none; font-size: 12px;">
|
||||
<a href="/posts/{{ selected_peer | replace(from="/", to="%2F") }}/{{ post.key | replace(from="/", to="%2F") }}">
|
||||
<code style="word-wrap: anywhere;{% if selected_post and post.key == selected_post %} font-weight: bold;{% endif %}">{{ post.key }}</code>
|
||||
| {{ post.date }}
|
||||
</a>
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="content">
|
||||
{% if post %}
|
||||
{{ post.text }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,32 +0,0 @@
|
|||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>lykin</title>
|
||||
<meta name="description" content="lykin: an SSB tutorial application">
|
||||
<meta name="author" content="glyph">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<h1>lykin</h1>
|
||||
<form action="/subscribe" method="post" style="width: 500px;">
|
||||
<fieldset>
|
||||
<legend>Subscription Management</legend>
|
||||
<input type="text" id="public_key" name="public_key" size=53 maxlength=53><br>
|
||||
<label for="public_key">Public Key</label><br>
|
||||
<br>
|
||||
<input type="submit" value="Subscribe">
|
||||
<input type="submit" value="Unsubscribe" formaction="/unsubscribe">
|
||||
</fieldset>
|
||||
</form>
|
||||
<div>
|
||||
<h2>Subscriptions</h2>
|
||||
<ul>
|
||||
{% for feed in feeds -%}
|
||||
<li>{{ feed }}</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<div class="peers" style="text-align: center;">
|
||||
<ul style="padding-left: 0;">
|
||||
{% for peer in peers -%}
|
||||
<li style="list-style: none; font-size: 12px;">
|
||||
<a href="/posts/{{ peer | replace(from="/", to="%2F") }}">
|
||||
<code style="word-wrap: anywhere;{% if selected_peer and peer == selected_peer %} font-weight: bold;{% endif %}">{{ peer }}</code>
|
||||
</a>
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div class="content">
|
||||
{% if post %}
|
||||
{{ post.text | trim_start_matches(pat='"') | trim_end_matches(pat='"') | trim }}
|
||||
{% endif %}
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
<div class="posts">
|
||||
{% if posts %}
|
||||
<ul style="padding-left: 25px; padding-right: 25px;">
|
||||
{% for post in posts -%}
|
||||
<li style="list-style: none; font-size: 12px; margin-bottom: 5px;{% if selected_post and post.key == selected_post %} background-color: #FF6300;{% endif %}">
|
||||
<a class="flex-container" style="justify-content: space-between;{% if not post.read %} font-weight: bold;{% endif %}" href="/posts/{{ selected_peer | urlencode_strict }}/{{ post.key | urlencode_strict }}">
|
||||
<code style="word-wrap: anywhere;">{{ post.key }}</code>
|
||||
<p style="margin: 0;">{{ post.date }}</p>
|
||||
</a>
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -0,0 +1,21 @@
|
|||
<div class="nav">
|
||||
<div class="flex-container">
|
||||
<a href="/" title="Download latest posts"><img src="/icons/download.png" style="width: 55px;"></a>
|
||||
{% if post_is_selected %}
|
||||
{% if post.read %}
|
||||
{% set mark_unread_url = "/posts/" ~ selected_peer_encoded ~ "/" ~ selected_post_encoded ~ "/unread" %}
|
||||
<a href={{ mark_unread_url }} style="margin-left: 20px;" title="Mark as unread"><img src="/icons/unread_post.png" style="width: 55px;"></a>
|
||||
{% else %}
|
||||
{% set mark_read_url = "/posts/" ~ selected_peer_encoded ~ "/" ~ selected_post_encoded ~ "/read" %}
|
||||
<a href={{ mark_read_url }} style="margin-left: 20px;" title="Mark as read"><img src="/icons/read_post.png" style="width: 55px;"></a>
|
||||
{% endif %}
|
||||
<a href="/" style="margin-left: 20px;" title="Delete post"><img src="/icons/delete_post.png" style="width: 55px;"></a>
|
||||
{% endif %}
|
||||
<form class="flex-container" style="margin-left: auto; margin-right: 10px;" action="/subscribe" method="post">
|
||||
<label for="public_key">Public Key</label>
|
||||
<input type="text" id="public_key" name="public_key" size=50 maxlength=53>
|
||||
<input type="submit" value="Subscribe">
|
||||
<input type="submit" value="Unsubscribe" formaction="/unsubscribe">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue