no mass rerendering html plus logging fix

This commit is contained in:
Tyler Murphy 2023-01-29 00:35:06 -05:00
parent 9cbeee4b67
commit 7805c730e8
10 changed files with 142 additions and 89 deletions

View file

@ -2,7 +2,7 @@ body {
background-color: #f0f2f5; background-color: #f0f2f5;
} }
#posts { #posts, #create {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -172,7 +172,7 @@ body {
flex-direction: row; flex-direction: row;
} }
#comments input { .comments input {
all: unset; all: unset;
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 10px;

View file

@ -20,6 +20,5 @@ function header(home, people) {
</div> </div>
<div class="spacer"></div> <div class="spacer"></div>
` `
append(html)
add(html, 'header')
} }

View file

@ -41,19 +41,19 @@ function parsePost(post) {
</span> </span>
<div class="fullline nb"></div> <div class="fullline nb"></div>
<div class="postbuttons"> <div class="postbuttons">
<span onclick="like(this)"> <span class="likeclicky" onclick="like(this)">
<i class="icons like ${post.likes.includes(data.user.user_id) ? 'blue' : ''}"></i> <i class="liketoggle icons like ${post.likes.includes(data.user.user_id) ? 'blue' : ''}"></i>
<span class="bold ${post.likes.includes(data.user.user_id) ? 'blue' : ''}">Like</span> <span class="liketoggle bold ${post.likes.includes(data.user.user_id) ? 'blue' : ''}">Like</span>
</span> </span>
<span onclick="this.parentElement.parentElement.getElementsByClassName('newcomment')[0].focus()"> <span onclick="this.parentElement.parentElement.getElementsByClassName('newcomment')[0].focus()">
<i class="icons comm"></i> <i class="icons comm"></i>
<span class="bold">Comment</span> <span class="bold">Comment</span>
</span> </span>
</div> </div>
<div id="comments"> <div class="comments">
<div class="fullline" style="margin-top: 0"></div> <div class="fullline" style="margin-top: 0"></div>
${post.comments.map(parseComment).join('')} ${post.comments.map(parseComment).join('')}
<div class="comment"> <div class="comment commentsubmit">
<a class="pfp" href="profile"> <a class="pfp" href="profile">
</a> </a>
@ -64,7 +64,6 @@ function parsePost(post) {
</div> </div>
</div> </div>
` `
return html return html
} }
@ -78,7 +77,8 @@ function getPost(post_id) {
} }
async function like(span) { async function like(span) {
const id = parseInt(span.parentElement.parentElement.getAttribute('postid')) const container = span.parentElement.parentElement;
const id = parseInt(container.getAttribute('postid'))
const post = data.posts[getPost(id)] const post = data.posts[getPost(id)]
const index = post.likes.indexOf(data.user.user_id) const index = post.likes.indexOf(data.user.user_id)
const current = index !== -1 const current = index !== -1
@ -89,26 +89,40 @@ async function like(span) {
} else { } else {
post.likes.push(data.user.user_id) post.likes.push(data.user.user_id)
} }
render() const buttons = container
.getElementsByClassName("postbuttons")[0]
.getElementsByClassName("likeclicky")[0]
.getElementsByClassName("liketoggle")
if (current) {
buttons[0].classList.remove("blue")
buttons[1].classList.remove("blue")
} else {
buttons[0].classList.add("blue")
buttons[1].classList.add("blue")
}
} }
async function comment(event) { async function comment(event) {
event.preventDefault(); event.preventDefault();
const text = event.target.elements.text.value.trim(); const text = event.target.elements.text.value.trim();
if (text.length < 1) return; if (text.length < 1) return;
const id = parseInt(event.target.parentElement.parentElement.parentElement.getAttribute('postid')) const container = event.target.parentElement.parentElement.parentElement;
var index = getPost(id); const post_id = parseInt(container.getAttribute('postid'))
var index = getPost(post_id);
if (index === -1) return; if (index === -1) return;
const response = await postcomment(id, text) const response = await postcomment(post_id, text)
if (response.status != 200) return; if (response.status != 200) return;
event.target.elements.text.value = ''; event.target.elements.text.value = '';
data.posts[index].comments.push([data.user.user_id, text]) let new_comment = [data.user.user_id, text]
render() data.posts[index].comments.push(new_comment)
let comments = container.getElementsByClassName("comments")[0]
prepend(parseComment(new_comment), comments, comments.getElementsByClassName("commentsubmit")[0])
} }
async function post() { async function post() {
const text = document.getElementById("text").value.trim() const text = document.getElementById("text").value.trim()
const error = document.getElementsByClassName('error')[0] const error = document.getElementsByClassName('error')[0]
const posts_block = document.getElementById("posts")
if (text.length < 1) return; if (text.length < 1) return;
const response = await createpost(text); const response = await createpost(text);
if (response.status != 201) { if (response.status != 201) {
@ -116,20 +130,32 @@ async function post() {
return; return;
} }
error.innerHTML = ''; error.innerHTML = '';
data.posts.unshift({ let post = {
post_id: response.json.post_id, post_id: response.json.post_id,
user_id: data.user.user_id, user_id: data.user.user_id,
date: Date.now(), date: Date.now(),
content: text, content: text,
likes: [], likes: [],
comments: [] comments: []
}) }
render() data.posts.unshift(post)
let html = parsePost(post)
prepend(html, posts_block)
document.getElementById('popup').classList.add('hidden')
}
async function loadMore() {
const posts = await load()
data.posts.push(... posts)
const posts_block = document.getElementById("posts")
for (p of posts) {
append(parsePost(p), posts_block)
}
} }
function render() { function render() {
const html = ` const html = `
<div id="posts"> <div id="create">
<div class="create"> <div class="create">
<a class="pfp" href="profile"> <a class="pfp" href="profile">
@ -140,11 +166,13 @@ function render() {
</p> </p>
</button> </button>
</div> </div>
</div>
<div id="posts">
${data.posts.map(p => parsePost(p)).join('')} ${data.posts.map(p => parsePost(p)).join('')}
</div> </div>
` `
add(html, 'posts') append(html)
const popup = ` const popup = `
<div id="popup" class="hidden"> <div id="popup" class="hidden">
@ -168,19 +196,15 @@ function render() {
</div> </div>
` `
add(popup, 'popup') append(popup)
const load = ` const load = `
<div id="load"> <div id="load">
<a class="bold gtext" onclick="load()">Load more posts</a> <a class="bold gtext" onclick="loadMore()">Load more posts</a>
</div> </div>
` `
if (page !== -1) { append(load)
add(load, 'load')
} else {
remove('load')
}
} }
var page = 0 var page = 0
@ -194,10 +218,11 @@ async function load() {
const posts = (await loadpostspage(page)).json const posts = (await loadpostspage(page)).json
if (posts.length === 0) { if (posts.length === 0) {
page = -1 page = -1
remove('load')
return []
} else { } else {
page++ page++
} }
data.posts.push(... posts)
const batch = [] const batch = []
for (const post of posts) { for (const post of posts) {
for(const comment of post.comments) { for(const comment of post.comments) {
@ -209,17 +234,18 @@ async function load() {
if (batch.includes(post.user_id)) continue if (batch.includes(post.user_id)) continue
batch.push(post.user_id) batch.push(post.user_id)
} }
const users = (await loadusers(batch)).json const users = batch.length == 0 ? [] : (await loadusers(batch)).json
for (const user of users) { for (const user of users) {
data.users[user.user_id] = user data.users[user.user_id] = user
} }
render() return posts
} }
async function init() { async function init() {
header(true, false) header(true, false)
data.user = (await loadself()).json data.user = (await loadself()).json
data.users[data.user.user_id] = data.user data.users[data.user.user_id] = data.user
load() const posts = await load()
data.posts.push(... posts)
render()
} }

View file

@ -1,15 +1,26 @@
var range; function prepend(html, container, before) {
if (container === undefined) {
container = document.body
}
if (before === undefined) {
before = container.firstChild
}
console.log(html, container, before)
var range = document.createRange()
range.setStart(container, 0);
container.insertBefore(
range.createContextualFragment(html),
before
)
}
function add(html, id) { function append(html, container) {
const old = document.getElementById(id) if (container === undefined) {
if (old !== null) { container = document.body
old.remove()
} }
if (range === undefined) { var range = document.createRange()
var range = document.createRange() range.setStart(container, 0);
range.setStart(document.body, 0) container.appendChild(
}
document.body.appendChild(
range.createContextualFragment(html) range.createContextualFragment(html)
) )
} }

View file

@ -23,19 +23,15 @@ function render() {
</div> </div>
` `
add(html, 'users') append(html)
const load = ` const load = `
<div id="load"> <div id="load">
<a class="bold gtext" onclick="load()">Load more users</a> <a class="bold gtext" onclick="loadMore()">Load more users</a>
</div> </div>
` `
if (page !== -1) { append(load)
add(load, 'load')
} else {
remove('load')
}
} }
var page = 0 var page = 0
@ -43,16 +39,30 @@ var data = {
users: [] users: []
} }
async function loadMore() {
let users = await load()
const users_block = document.getElementById("users")
for (user of users) {
append(parseUser(user), users_block)
}
}
async function load() { async function load() {
const users = (await loaduserspage(page)).json const users = (await loaduserspage(page)).json
if (users.length === 0) { if (users.length === 0) {
page = -1 page = -1
remove('load')
} else { } else {
page++ page++
} }
return users
}
async function init() {
let users = await load()
data.users.push(... users) data.users.push(... users)
render() render()
} }
header(false, true) header(false, true)
load() init()

View file

@ -1,3 +1,21 @@
function swap(value) {
let postsb = document.getElementById("profilepostbutton");
let aboutb = document.getElementById("profileaboutbutton");
let posts = document.getElementById("posts");
let about = document.getElementById("about");
if (value) {
postsb.classList.add("selected")
aboutb.classList.remove("selected")
about.classList.add("hidden")
posts.classList.remove("hidden")
} else {
postsb.classList.remove("selected")
aboutb.classList.add("selected")
about.classList.remove("hidden")
posts.classList.add("hidden")
}
}
function render() { function render() {
const html = ` const html = `
<div id="top"> <div id="top">
@ -17,10 +35,10 @@ function render() {
</div> </div>
<div class="fullline" style="width: 80em; margin-bottom: 0;"></div> <div class="fullline" style="width: 80em; margin-bottom: 0;"></div>
<div class="profilebuttons"> <div class="profilebuttons">
<button class="${posts ? 'selected' : ''}" onclick="posts = true; render()"> <button id="profilepostbutton" class="${posts ? 'selected' : ''}" onclick="swap(true)">
Posts Posts
</button> </button>
<button class="${posts ? '' : 'selected'}" onclick="posts = false; render()"> <button id="profileaboutbutton" class="${posts ? '' : 'selected'}" onclick="swap(false)">
About About
</button> </button>
<div style="flex: 20"></div> <div style="flex: 20"></div>
@ -29,7 +47,7 @@ function render() {
</div> </div>
` `
add(html, 'top') append(html)
const postsh = ` const postsh = `
<div id="posts" class="${posts ? '' : 'hidden'}"> <div id="posts" class="${posts ? '' : 'hidden'}">
@ -37,7 +55,7 @@ function render() {
</div> </div>
` `
add(postsh, 'posts') append(postsh)
const about = ` const about = `
<div id="about" class="post ${posts ? 'hidden' : ''}"> <div id="about" class="post ${posts ? 'hidden' : ''}">
@ -52,7 +70,7 @@ function render() {
</div> </div>
` `
add(about, 'about') append(about)
} }
async function logout_button() { async function logout_button() {
@ -72,21 +90,22 @@ async function load() {
params[key] = value params[key] = value
} }
let self = (await loadself()).json; data.user = (await loadself()).json;
data.users[data.user.user_id] = data.user
let id; let id;
if (params.id !== undefined && !isNaN(params.id)) { if (params.id !== undefined && !isNaN(params.id)) {
id = parseInt(params.id); id = parseInt(params.id);
} else { } else {
id = self.user_id id = data.user.user_id
} }
isself = id === self.user_id isself = id === data.user.user_id
const posts = (await loadusersposts(id)).json const posts = (await loadusersposts(id)).json
data.posts.push(... posts) data.posts.push(... posts)
const batch = [id] const batch = []
for (const post of posts) { for (const post of posts) {
for(const comment of post.comments) { for(const comment of post.comments) {
if (data.users[comment[0]] !== undefined) continue if (data.users[comment[0]] !== undefined) continue
@ -97,11 +116,10 @@ async function load() {
if (batch.includes(post.user_id)) continue if (batch.includes(post.user_id)) continue
batch.push(post.user_id) batch.push(post.user_id)
} }
const users = (await loadusers(batch)).json const users = batch.length == 0 ? [] : (await loadusers(batch)).json
for (const user of users) { for (const user of users) {
data.users[user.user_id] = user data.users[user.user_id] = user
} }
data.user = data.users[id]
render() render()
} }

View file

@ -6,10 +6,10 @@ use axum::{
use crate::{ use crate::{
console, console,
types::{extract::AuthorizedUser, http::ResponseCode}, types::{extract::{AuthorizedUser, Log}, http::ResponseCode},
}; };
async fn root(user: Option<AuthorizedUser>) -> Response { async fn root(user: Option<AuthorizedUser>, _: Log) -> Response {
if user.is_some() { if user.is_some() {
Redirect::to("/home").into_response() Redirect::to("/home").into_response()
} else { } else {
@ -17,7 +17,7 @@ async fn root(user: Option<AuthorizedUser>) -> Response {
} }
} }
async fn login(user: Option<AuthorizedUser>) -> Response { async fn login(user: Option<AuthorizedUser>, _: Log) -> Response {
if user.is_some() { if user.is_some() {
Redirect::to("/home").into_response() Redirect::to("/home").into_response()
} else { } else {
@ -25,7 +25,7 @@ async fn login(user: Option<AuthorizedUser>) -> Response {
} }
} }
async fn home(user: Option<AuthorizedUser>) -> Response { async fn home(user: Option<AuthorizedUser>, _: Log) -> Response {
if user.is_none() { if user.is_none() {
Redirect::to("/login").into_response() Redirect::to("/login").into_response()
} else { } else {
@ -33,7 +33,7 @@ async fn home(user: Option<AuthorizedUser>) -> Response {
} }
} }
async fn people(user: Option<AuthorizedUser>) -> Response { async fn people(user: Option<AuthorizedUser>, _: Log) -> Response {
if user.is_none() { if user.is_none() {
Redirect::to("/login").into_response() Redirect::to("/login").into_response()
} else { } else {
@ -41,7 +41,7 @@ async fn people(user: Option<AuthorizedUser>) -> Response {
} }
} }
async fn profile(user: Option<AuthorizedUser>) -> Response { async fn profile(user: Option<AuthorizedUser>, _: Log) -> Response {
if user.is_none() { if user.is_none() {
Redirect::to("/login").into_response() Redirect::to("/login").into_response()
} else { } else {
@ -53,7 +53,7 @@ async fn console() -> Response {
console::generate().await console::generate().await
} }
async fn wordpress() -> Response { async fn wordpress(_: Log) -> Response {
ResponseCode::ImATeapot.text("Hello i am a teapot owo") ResponseCode::ImATeapot.text("Hello i am a teapot owo")
} }

View file

@ -45,9 +45,6 @@ lazy_static! {
} }
pub async fn log(ip: IpAddr, method: Method, uri: Uri, path: Option<String>, body: Option<String>) { pub async fn log(ip: IpAddr, method: Method, uri: Uri, path: Option<String>, body: Option<String>) {
if uri.to_string().starts_with("/console") {
return;
}
let path = path.unwrap_or_default(); let path = path.unwrap_or_default();
let body = body.unwrap_or_default(); let body = body.unwrap_or_default();

View file

@ -1,11 +1,11 @@
use axum::{ use axum::{
body::HttpBody, body::HttpBody,
extract::ConnectInfo,
http::{Request, StatusCode}, http::{Request, StatusCode},
middleware::{self, Next}, middleware::{self, Next},
response::Response, response::Response,
Extension, RequestExt, Router, Extension, RequestExt, Router,
}; };
use axum_client_ip::ClientIp;
use std::{net::SocketAddr, process::exit}; use std::{net::SocketAddr, process::exit};
use tower_cookies::CookieManagerLayer; use tower_cookies::CookieManagerLayer;
use tracing::{error, info, metadata::LevelFilter}; use tracing::{error, info, metadata::LevelFilter};
@ -40,18 +40,11 @@ async fn log<B>(mut req: Request<B>, next: Next<B>) -> Response
where where
B: Send + Sync + 'static + HttpBody, B: Send + Sync + 'static + HttpBody,
{ {
let Ok(ConnectInfo(info)) = req.extract_parts::<ConnectInfo<SocketAddr>>().await else { let Ok(ClientIp(ip)) = req.extract_parts::<ClientIp>().await else {
return next.run(req).await return next.run(req).await
}; };
console::log( console::log(ip, req.method().clone(), req.uri().clone(), None, None).await;
info.ip(),
req.method().clone(),
req.uri().clone(),
None,
None,
)
.await;
next.run(req).await next.run(req).await
} }
@ -80,9 +73,9 @@ async fn main() {
let app = Router::new() let app = Router::new()
.fallback(not_found) .fallback(not_found)
.nest("/", pages::router())
.layer(middleware::from_fn(log)) .layer(middleware::from_fn(log))
.layer(middleware::from_fn(serve)) .layer(middleware::from_fn(serve))
.nest("/", pages::router())
.nest( .nest(
"/api/auth", "/api/auth",
auth::router().layer(Extension(RouterURI("/api/auth"))), auth::router().layer(Extension(RouterURI("/api/auth"))),

View file

@ -85,10 +85,9 @@ where
type Rejection = Response; type Rejection = Response;
async fn from_request(req: Request<B>, state: &S) -> Result<Self> { async fn from_request(req: Request<B>, state: &S) -> Result<Self> {
let body = match parse_body(req, state).await { let body = match parse_body(req, state).await {
Ok(body) => body, Ok(body) => body,
Err(err) => return Err(err) Err(err) => return Err(err),
}; };
let Ok(value) = serde_json::from_str::<T>(&body) else { let Ok(value) = serde_json::from_str::<T>(&body) else {
@ -128,7 +127,7 @@ where
B: HttpBody + Sync + Send + 'static, B: HttpBody + Sync + Send + 'static,
B::Data: Send, B::Data: Send,
B::Error: Into<BoxError>, B::Error: Into<BoxError>,
S: Send + Sync S: Send + Sync,
{ {
let Ok(ClientIp(ip)) = req.extract_parts::<ClientIp>().await else { let Ok(ClientIp(ip)) = req.extract_parts::<ClientIp>().await else {
tracing::error!("Failed to read client ip"); tracing::error!("Failed to read client ip");