Merge pull request 'Add SSB routes for newly added templates' (#27) from ssb_routes into main
Reviewed-on: #27
This commit is contained in:
commit
7f09cc5888
|
@ -30,15 +30,15 @@ pub mod routes;
|
|||
mod tests;
|
||||
pub mod utils;
|
||||
|
||||
use log::{info, error};
|
||||
use log::{error, info};
|
||||
use std::process;
|
||||
|
||||
use rocket::{catchers, routes, Rocket, Build, fs::FileServer};
|
||||
use rocket::{catchers, fs::FileServer, routes, Build, Rocket};
|
||||
use rocket_dyn_templates::Template;
|
||||
|
||||
use crate::routes::authentication::*;
|
||||
use crate::routes::device::*;
|
||||
use crate::routes::catchers::*;
|
||||
use crate::routes::device::*;
|
||||
use crate::routes::index::*;
|
||||
use crate::routes::ping::*;
|
||||
use crate::routes::scuttlebutt::*;
|
||||
|
@ -47,12 +47,27 @@ use crate::routes::settings::admin::*;
|
|||
use crate::routes::settings::dns::*;
|
||||
use crate::routes::settings::network::*;
|
||||
|
||||
|
||||
pub type BoxError = Box<dyn std::error::Error>;
|
||||
|
||||
/// Create rocket instance & mount all routes.
|
||||
fn init_rocket() -> Rocket<Build> {
|
||||
rocket::build()
|
||||
.mount(
|
||||
"/scuttlebutt",
|
||||
routes![
|
||||
peers, // WEB ROUTE
|
||||
friends, // WEB ROUTE
|
||||
follows, // WEB ROUTE
|
||||
followers, // WEB ROUTE
|
||||
blocks, // WEB ROUTE
|
||||
profile, // WEB ROUTE
|
||||
private, // WEB ROUTE
|
||||
follow, // WEB ROUTE
|
||||
unfollow, // WEB ROUTE
|
||||
block, // WEB ROUTE
|
||||
publish, // WEB ROUTE
|
||||
],
|
||||
)
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
|
@ -68,13 +83,10 @@ fn init_rocket() -> Rocket<Build> {
|
|||
login, // WEB ROUTE
|
||||
login_post, // WEB ROUTE
|
||||
logout, // WEB ROUTE
|
||||
messages, // WEB ROUTE
|
||||
network_home, // WEB ROUTE
|
||||
network_add_ssid, // WEB ROUTE
|
||||
network_add_wifi, // WEB ROUTE
|
||||
network_detail, // WEB ROUTE
|
||||
peers, // WEB ROUTE
|
||||
profile, // WEB ROUTE
|
||||
reboot_cmd, // WEB ROUTE
|
||||
shutdown_cmd, // WEB ROUTE
|
||||
shutdown_menu, // WEB ROUTE
|
||||
|
@ -130,7 +142,6 @@ fn init_rocket() -> Rocket<Build> {
|
|||
/// Launch the peach-web rocket server.
|
||||
#[rocket::main]
|
||||
async fn main() {
|
||||
|
||||
// initialize logger
|
||||
env_logger::init();
|
||||
|
||||
|
@ -140,7 +151,7 @@ async fn main() {
|
|||
|
||||
// launch rocket
|
||||
info!("Launching Rocket");
|
||||
if let Err(e) = rocket.launch().await {
|
||||
if let Err(e) = rocket.launch().await {
|
||||
error!("Error in Rocket application: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,30 @@
|
|||
//! Routes for ScuttleButt related functionality.
|
||||
//! Routes for Scuttlebutt related functionality.
|
||||
|
||||
use rocket::{get, request::FlashMessage};
|
||||
use rocket::{
|
||||
form::{Form, FromForm},
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
serde::{Deserialize, Serialize},
|
||||
uri,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::routes::authentication::Authenticated;
|
||||
|
||||
// HELPERS AND ROUTES FOR /messages
|
||||
// HELPERS AND ROUTES FOR /private
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct MessageContext {
|
||||
pub struct PrivateContext {
|
||||
pub back: Option<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl MessageContext {
|
||||
pub fn build() -> MessageContext {
|
||||
MessageContext {
|
||||
impl PrivateContext {
|
||||
pub fn build() -> PrivateContext {
|
||||
PrivateContext {
|
||||
back: None,
|
||||
flash_name: None,
|
||||
flash_msg: None,
|
||||
|
@ -27,9 +33,10 @@ impl MessageContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[get("/messages")]
|
||||
pub fn messages(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
let mut context = MessageContext::build();
|
||||
/// A private message composition and publication page.
|
||||
#[get("/private")]
|
||||
pub fn private(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
let mut context = PrivateContext::build();
|
||||
context.back = Some("/".to_string());
|
||||
context.title = Some("Private Messages".to_string());
|
||||
// check to see if there is a flash message to display
|
||||
|
@ -62,6 +69,7 @@ impl PeerContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// A peer menu which allows navigating to lists of friends, follows, followers and blocks.
|
||||
#[get("/peers")]
|
||||
pub fn peers(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
let mut context = PeerContext::build();
|
||||
|
@ -76,6 +84,94 @@ pub fn peers(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
|||
Template::render("peers", &context)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /post/publish
|
||||
|
||||
#[derive(Debug, Deserialize, FromForm)]
|
||||
pub struct Post {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
/// Publish a public Scuttlebutt post. Redirects to profile page of the PeachCloud local identity with a flash message describing the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/publish", data = "<post>")]
|
||||
pub fn publish(
|
||||
post: Form<Post>,
|
||||
flash: Option<FlashMessage>,
|
||||
_auth: Authenticated,
|
||||
) -> Flash<Redirect> {
|
||||
let post_text = &post.text;
|
||||
// perform the sbotcli publish action using post_text
|
||||
// if successful, redirect to home profile page and flash "success"
|
||||
// if error, redirect to home profile page and flash "error"
|
||||
// redirect to the profile template without public key ("home" / local profile)
|
||||
let pub_key: std::option::Option<&str> = None;
|
||||
let profile_url = uri!(profile(pub_key));
|
||||
// consider adding the message reference to the flash message (or render it in the template for
|
||||
// `profile`
|
||||
Flash::success(Redirect::to(profile_url), "Published public post")
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /follow
|
||||
|
||||
#[derive(Debug, Deserialize, FromForm)]
|
||||
pub struct PublicKey {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
/// Follow a Scuttlebutt profile specified by the given public key. Redirects to the appropriate profile page with a flash message describing the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/follow", data = "<pub_key>")]
|
||||
pub fn follow(
|
||||
pub_key: Form<PublicKey>,
|
||||
flash: Option<FlashMessage>,
|
||||
_auth: Authenticated,
|
||||
) -> Flash<Redirect> {
|
||||
let public_key = &pub_key.key;
|
||||
// perform the sbotcli follow action using &pub_key.0
|
||||
// if successful, redirect to profile page with provided public key and flash "success"
|
||||
// if error, redirect to profile page with provided public key and flash "error"
|
||||
// redirect to the profile template with provided public key
|
||||
let profile_url = uri!(profile(Some(public_key)));
|
||||
let success_msg = format!("Followed {}", public_key);
|
||||
Flash::success(Redirect::to(profile_url), success_msg)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /unfollow
|
||||
|
||||
/// Unfollow a Scuttlebutt profile specified by the given public key. Redirects to the appropriate profile page with a flash message describing the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/unfollow", data = "<pub_key>")]
|
||||
pub fn unfollow(
|
||||
pub_key: Form<PublicKey>,
|
||||
flash: Option<FlashMessage>,
|
||||
_auth: Authenticated,
|
||||
) -> Flash<Redirect> {
|
||||
let public_key = &pub_key.key;
|
||||
// perform the sbotcli unfollow action using &pub_key.0
|
||||
// if successful, redirect to profile page with provided public key and flash "success"
|
||||
// if error, redirect to profile page with provided public key and flash "error"
|
||||
// redirect to the profile template with provided public key
|
||||
let profile_url = uri!(profile(Some(public_key)));
|
||||
let success_msg = format!("Unfollowed {}", public_key);
|
||||
Flash::success(Redirect::to(profile_url), success_msg)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /block
|
||||
|
||||
/// Block a Scuttlebutt profile specified by the given public key. Redirects to the appropriate profile page with a flash message describing the outcome of the action (may be successful or unsuccessful).
|
||||
#[post("/block", data = "<pub_key>")]
|
||||
pub fn block(
|
||||
pub_key: Form<PublicKey>,
|
||||
flash: Option<FlashMessage>,
|
||||
_auth: Authenticated,
|
||||
) -> Flash<Redirect> {
|
||||
let public_key = &pub_key.key;
|
||||
// perform the sbotcli block action using &pub_key.0
|
||||
// if successful, redirect to profile page with provided public key and flash "success"
|
||||
// if error, redirect to profile page with provided public key and flash "error"
|
||||
// redirect to the profile template with provided public key
|
||||
let profile_url = uri!(profile(Some(public_key)));
|
||||
let success_msg = format!("Blocked {}", public_key);
|
||||
Flash::success(Redirect::to(profile_url), success_msg)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /profile
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
@ -97,8 +193,13 @@ impl ProfileContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[get("/profile")]
|
||||
pub fn profile(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
/// A Scuttlebutt profile, specified by a public key. It may be our own profile or the profile of a peer. If not public key query parameter is provided, the local profile is displayed (ie. the profile of the public key associated with the local PeachCloud device).
|
||||
#[get("/profile?<pub_key>")]
|
||||
pub fn profile(
|
||||
pub_key: Option<&str>,
|
||||
flash: Option<FlashMessage>,
|
||||
_auth: Authenticated,
|
||||
) -> Template {
|
||||
let mut context = ProfileContext::build();
|
||||
context.back = Some("/".to_string());
|
||||
context.title = Some("Profile".to_string());
|
||||
|
@ -110,3 +211,155 @@ pub fn profile(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
|||
};
|
||||
Template::render("profile", &context)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /friends
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct FriendsContext {
|
||||
pub back: Option<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl FriendsContext {
|
||||
pub fn build() -> FriendsContext {
|
||||
FriendsContext {
|
||||
back: None,
|
||||
flash_name: None,
|
||||
flash_msg: None,
|
||||
title: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of friends (mutual follows), with each list item displaying the name, image and public
|
||||
/// key of the peer.
|
||||
#[get("/friends")]
|
||||
pub fn friends(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
let mut context = FriendsContext::build();
|
||||
context.back = Some("/scuttlebutt/peers".to_string());
|
||||
context.title = Some("Friends".to_string());
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
Template::render("peers_list", &context)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /follows
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct FollowsContext {
|
||||
pub back: Option<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl FollowsContext {
|
||||
pub fn build() -> FollowsContext {
|
||||
FollowsContext {
|
||||
back: None,
|
||||
flash_name: None,
|
||||
flash_msg: None,
|
||||
title: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of follows (peers we follow who do not follow us), with each list item displaying the name, image and public
|
||||
/// key of the peer.
|
||||
#[get("/follows")]
|
||||
pub fn follows(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
let mut context = FollowsContext::build();
|
||||
context.back = Some("/scuttlebutt/peers".to_string());
|
||||
context.title = Some("Follows".to_string());
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
Template::render("peers_list", &context)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /followers
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct FollowersContext {
|
||||
pub back: Option<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl FollowersContext {
|
||||
pub fn build() -> FollowersContext {
|
||||
FollowersContext {
|
||||
back: None,
|
||||
flash_name: None,
|
||||
flash_msg: None,
|
||||
title: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of followers (peers who follow us but who we do not follow), with each list item displaying the name, image and public
|
||||
/// key of the peer.
|
||||
#[get("/followers")]
|
||||
pub fn followers(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
let mut context = FollowersContext::build();
|
||||
context.back = Some("/scuttlebutt/peers".to_string());
|
||||
context.title = Some("Followers".to_string());
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
Template::render("peers_list", &context)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /blocks
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct BlocksContext {
|
||||
pub back: Option<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl BlocksContext {
|
||||
pub fn build() -> BlocksContext {
|
||||
BlocksContext {
|
||||
back: None,
|
||||
flash_name: None,
|
||||
flash_msg: None,
|
||||
title: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of blocks (peers we've blocked previously), with each list item displaying the name, image and public
|
||||
/// key of the peer.
|
||||
#[get("/blocks")]
|
||||
pub fn blocks(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
let mut context = BlocksContext::build();
|
||||
context.back = Some("/scuttlebutt/peers".to_string());
|
||||
context.title = Some("Blocks".to_string());
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.flash_name = Some(flash.kind().to_string());
|
||||
context.flash_msg = Some(flash.message().to_string());
|
||||
};
|
||||
Template::render("peers_list", &context)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use rocket::serde::json::{Value, json};
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::local::blocking::Client;
|
||||
use rocket::serde::json::{json, Value};
|
||||
|
||||
use crate::utils::build_json_response;
|
||||
|
||||
|
@ -46,7 +46,7 @@ fn index_html() {
|
|||
let body = response.into_string().unwrap();
|
||||
assert!(body.contains("/peers"));
|
||||
assert!(body.contains("/profile"));
|
||||
assert!(body.contains("/messages"));
|
||||
assert!(body.contains("/private"));
|
||||
assert!(body.contains("/device"));
|
||||
assert!(body.contains("/help"));
|
||||
assert!(body.contains("/network"));
|
||||
|
@ -154,9 +154,9 @@ fn login_html() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn messages_html() {
|
||||
fn private_html() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client.get("/messages").dispatch();
|
||||
let response = client.get("/scuttlebutt/private").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||
let body = response.into_string().unwrap();
|
||||
|
@ -166,17 +166,102 @@ fn messages_html() {
|
|||
#[test]
|
||||
fn peers_html() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client.get("/peers").dispatch();
|
||||
let response = client.get("/scuttlebutt/peers").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||
let body = response.into_string().unwrap();
|
||||
assert!(body.contains("Scuttlebutt Peers"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn friends_html() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client.get("/scuttlebutt/friends").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||
let body = response.into_string().unwrap();
|
||||
assert!(body.contains("Friends"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn follows_html() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client.get("/scuttlebutt/follows").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||
let body = response.into_string().unwrap();
|
||||
assert!(body.contains("Follows"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn followers_html() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client.get("/scuttlebutt/followers").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||
let body = response.into_string().unwrap();
|
||||
assert!(body.contains("Followers"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_html() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client.get("/scuttlebutt/blocks").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||
let body = response.into_string().unwrap();
|
||||
assert!(body.contains("Blocks"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn follow() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client
|
||||
.post("/scuttlebutt/follow")
|
||||
.header(ContentType::Form)
|
||||
.body("key=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519")
|
||||
.dispatch();
|
||||
// ensure we redirect (303)
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unfollow() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client
|
||||
.post("/scuttlebutt/unfollow")
|
||||
.header(ContentType::Form)
|
||||
.body("key=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519")
|
||||
.dispatch();
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client
|
||||
.post("/scuttlebutt/block")
|
||||
.header(ContentType::Form)
|
||||
.body("key=HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519")
|
||||
.dispatch();
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn publish_post() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client
|
||||
.post("/scuttlebutt/publish")
|
||||
.header(ContentType::Form)
|
||||
.body("text='golden ripples in the meshwork'")
|
||||
.dispatch();
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn profile_html() {
|
||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||
let response = client.get("/profile").dispatch();
|
||||
let response = client.get("/scuttlebutt/profile").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||
let body = response.into_string().unwrap();
|
||||
|
@ -432,7 +517,6 @@ fn test_build_json_response() {
|
|||
assert_eq!(j["msg"], json!(null));
|
||||
}
|
||||
|
||||
|
||||
// FILE TESTS
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
<div class="grid">
|
||||
<!-- top-left -->
|
||||
<!-- PEERS LINK AND ICON -->
|
||||
<a class="top-left" href="/peers" title="Scuttlebutt Peers">
|
||||
<a class="top-left" href="/scuttlebutt/peers" title="Scuttlebutt Peers">
|
||||
<div class="circle circle-small">
|
||||
<img class="icon-medium" src="icons/users.svg">
|
||||
</div>
|
||||
</a>
|
||||
<!-- top-middle -->
|
||||
<!-- CURRENT USER LINK AND ICON -->
|
||||
<a class="top-middle" href="/profile" title="Profile">
|
||||
<a class="top-middle" href="/scuttlebutt/profile" title="Profile">
|
||||
<div class="circle circle-small">
|
||||
<img class="icon-medium" src="icons/user.svg">
|
||||
</div>
|
||||
</a>
|
||||
<!-- top-right -->
|
||||
<!-- MESSAGES LINK AND ICON -->
|
||||
<a class="top-right" href="/messages" title="Private Messages">
|
||||
<a class="top-right" href="/scuttlebutt/private" title="Private Messages">
|
||||
<div class="circle circle-small">
|
||||
<img class="icon-medium" src="icons/envelope.svg">
|
||||
</div>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<input id="publishPost" class="button button-primary center" title="Publish" type="submit" value="Publish">
|
||||
</form>
|
||||
<!-- BUTTONS -->
|
||||
<!-- TODO: each of these buttons needs to be a form with a public key -->
|
||||
<div id="buttons">
|
||||
<a id="followPeer" class="button button-primary center" href="/scuttlebutt/follow" title="Follow Peer">Follow</a>
|
||||
<a id="blockPeer" class="button button-warning center" href="/scuttlebutt/block" title="Block Peer">Block</a>
|
||||
|
|
Loading…
Reference in New Issue