Add basic post feed & ability to make posts
parent
8d7ea49b6d
commit
b21a76385f
100
src/main.rs
100
src/main.rs
|
@ -1,10 +1,12 @@
|
|||
#[macro_use]
|
||||
extern crate rocket;
|
||||
use std::sync::Mutex;
|
||||
// This trait is required when dealing with streams.
|
||||
use async_std::stream::StreamExt;
|
||||
|
||||
use golgi::{
|
||||
api::get_subset::{SubsetQuery, SubsetQueryOptions},
|
||||
messages::SsbMessageContent,
|
||||
messages::{SsbMessageContent, SsbMessageContentType, SsbMessageValue},
|
||||
GolgiError, Sbot,
|
||||
};
|
||||
use rocket::form::Form;
|
||||
|
@ -12,6 +14,11 @@ use rocket::fs::FileServer;
|
|||
use rocket::serde::{json::Json, Serialize};
|
||||
use rocket::State;
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct Message<'r> {
|
||||
r#message_text: &'r str,
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct ProfileUpdate<'r> {
|
||||
r#username: &'r str,
|
||||
|
@ -26,11 +33,97 @@ struct Whoami {
|
|||
pubkey: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
struct Feed {
|
||||
posts: Vec<Post>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
struct Post {
|
||||
author: String,
|
||||
timestamp: f64,
|
||||
hash: String,
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
||||
|
||||
#[get("/posts")]
|
||||
async fn get_posts() -> Json<Feed> {
|
||||
// Initialize SSB data
|
||||
let mut sbot_client = Sbot::init(None, None).await.unwrap();
|
||||
let ssb_id = sbot_client.whoami().await.unwrap();
|
||||
|
||||
let history_stream = sbot_client.create_history_stream(ssb_id).await.unwrap();
|
||||
|
||||
// Iterate through the elements in the stream and use `map` to convert
|
||||
// each `SsbMessageValue` element into a tuple of
|
||||
// `(String, SsbMessageContentType)`. This is an example of stream
|
||||
// conversion.
|
||||
let type_stream = history_stream.map(|msg| match msg {
|
||||
Ok(val) => {
|
||||
let message_type = val.get_message_type()?;
|
||||
let structure = Post {
|
||||
author: val.author,
|
||||
timestamp: val.timestamp,
|
||||
hash: val.hash,
|
||||
};
|
||||
let tuple: (Post, SsbMessageContentType) = (structure, message_type);
|
||||
Ok(tuple)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
});
|
||||
|
||||
// Pin the stream to the stack to allow polling of the `future`.
|
||||
futures::pin_mut!(type_stream);
|
||||
|
||||
println!("looping through type stream");
|
||||
|
||||
let mut posts: Vec<Post> = Vec::new();
|
||||
// Iterate through each element in the stream and match on the `Result`.
|
||||
// In this case, each element has type
|
||||
// `Result<(String, SsbMessageContentType), GolgiError>`.
|
||||
while let Some(res) = type_stream.next().await {
|
||||
match res {
|
||||
Ok(value) => {
|
||||
if value.1 == SsbMessageContentType::Post {
|
||||
println!(
|
||||
"author: {}, timestamp: {}, signature: {}",
|
||||
value.0.author, value.0.timestamp, value.0.hash
|
||||
);
|
||||
posts.push(value.0);
|
||||
} else {
|
||||
println!("{:?}", value.1);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
println!("err: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Json(Feed { posts })
|
||||
}
|
||||
|
||||
#[post("/post", data = "<message>")]
|
||||
async fn new_post(message: Form<Message<'_>>) -> Option<String> {
|
||||
// Initialize SSB data
|
||||
let mut sbot_client = Sbot::init(None, None).await.unwrap();
|
||||
|
||||
// We can also match on the returned `Result`, rather than using the `?` operator.
|
||||
match sbot_client.publish_post(message.message_text).await {
|
||||
Ok(post_ref) => Some(post_ref),
|
||||
Err(e) => {
|
||||
eprintln!("failed to publish post: {}", e);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/update", data = "<profileupdate>")]
|
||||
async fn profile_update(profileupdate: Form<ProfileUpdate<'_>>) -> String {
|
||||
// Initialize SSB data
|
||||
|
@ -80,7 +173,10 @@ async fn whoami() -> Json<Whoami> {
|
|||
#[rocket::main]
|
||||
async fn main() {
|
||||
let result = rocket::build()
|
||||
.mount("/api", routes![index, whoami, profile_update])
|
||||
.mount(
|
||||
"/api",
|
||||
routes![index, whoami, profile_update, get_posts, new_post],
|
||||
)
|
||||
.mount("/", FileServer::from("static/"))
|
||||
.launch()
|
||||
.await;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel=icon href="/favicon.png">
|
||||
<title>WebButt</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/profile">Profile</a></li>
|
||||
<li><a href="/feed">Home Feed</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h1>Feed</h1>
|
||||
<div id="messages">
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<form id="messageForm" class="form">
|
||||
<div class="form">
|
||||
<textarea name="message_text" id="message_text" cols="100" rows="5"></textarea>
|
||||
</div>
|
||||
<div class="form">
|
||||
<input type="submit" value="Post">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script src="zepto.js"></script>
|
||||
<script src="ssb.js"></script>
|
||||
<script>
|
||||
feed()
|
||||
window.addEventListener( "load", function () {
|
||||
function sendData() {
|
||||
const XHR = new XMLHttpRequest();
|
||||
|
||||
// Bind the FormData object and the form element
|
||||
const FD = new FormData( form );
|
||||
|
||||
// Define what happens on successful data submission
|
||||
XHR.addEventListener( "load", function(event) {
|
||||
alert( "Message Cypherlink: " + event.target.responseText );
|
||||
} );
|
||||
|
||||
// Define what happens in case of error
|
||||
XHR.addEventListener( "error", function( event ) {
|
||||
alert( 'Oops! Something went wrong.' );
|
||||
} );
|
||||
|
||||
// Set up our request
|
||||
XHR.open( "POST", "/api/post" );
|
||||
|
||||
// The data sent is what the user provided in the form
|
||||
XHR.send( FD );
|
||||
}
|
||||
|
||||
// Access the form element...
|
||||
const form = document.getElementById( "messageForm" );
|
||||
|
||||
// ...and take over its submit event.
|
||||
form.addEventListener( "submit", function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
sendData();
|
||||
feed();
|
||||
} );
|
||||
} );
|
||||
</script>
|
||||
<noscript>
|
||||
<p>Sorry, but this app requires JS to function :/</p>
|
||||
</noscript>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue