Add settings page, improve navigation

This commit is contained in:
asonix 2021-01-07 22:44:43 -06:00
parent f268759bee
commit 46e45e8b68
33 changed files with 678 additions and 245 deletions

View file

@ -23,7 +23,7 @@ hyaenidae-profiles = { version = "0.1.0", path = "../profiles" }
hyaenidae-toolkit = { version = "0.1.0", path = "../toolkit" }
log = "0.4"
mime = "0.3.16"
minify-html = "0.3.9"
minify-html = "0.4.0"
rand = "0.7"
once_cell = "1.5.2"
serde = { version = "1.0", features = ["derive"] }

View file

@ -1,8 +1,53 @@
.error {
color: #c92a60;
font-style: italic;
}
.top-bar__mobile {
display: none;
justify-content: space-between;
align-items: center;
background-color: #333;
padding: 8px 16px;
border-bottom: 3px solid #555;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
a {
color: #fff;
&:hover,
&:focus,
&:active {
color: #e5e5e5;
}
}
h1, h2, h3, h4, h5, h6 {
font-family: sans-serif;
margin: 0;
}
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #222;
padding: 8px 16px;
border-bottom: 2px solid #999;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
h1, h2, h3, h4, h5, h6 {
font-family: sans-serif;
margin: 0;
}
}
.home-content {
padding: 32px 0;
}
.account-page {
margin-bottom: 32px;
}
@ -13,6 +58,10 @@
color: #f5f5f5;
overflow: hidden;
&.card-top {
border-radius: 3px 3px 0 0;
}
&.standalone {
margin: 0;
border-radius: 3px;
@ -118,6 +167,13 @@
}
@media (max-width: 700px) {
.top-bar {
display: none;
}
.top-bar__mobile {
display: flex;
}
.columns {
flex-direction: column;

View file

@ -4,9 +4,9 @@ use crate::{
};
use actix_web::{http::header::LOCATION, web, HttpResponse, Scope};
use hyaenidae_accounts::{
Authenticated, CookiesArgs, CookiesPageArgs, DeleteUserArgs, DeleteUserPageArgs, LoginArgs,
LoginPageArgs, LogoutArgs, Pages as _, RegisterArgs, RegisterPageArgs, UpdatePasswordArgs,
UpdatePasswordPageArgs, UpdateUsernameArgs, UpdateUsernamePageArgs,
CookiesArgs, CookiesPageArgs, DeleteUserArgs, DeleteUserPageArgs, LoginArgs, LoginPageArgs,
LogoutArgs, LogoutState, Pages as _, RegisterArgs, RegisterPageArgs, UpdatePasswordArgs,
UpdatePasswordPageArgs, UpdateUsernameArgs, UpdateUsernamePageArgs, User,
};
pub(crate) struct Pages;
@ -109,7 +109,7 @@ async fn cookies_page(
};
rendered(HttpResponse::Ok(), |cursor| {
crate::templates::cookies(cursor, &cookie_state)
crate::templates::session::cookies(cursor, &cookie_state)
})
.state(&state)
}
@ -128,7 +128,7 @@ async fn login_page(
};
rendered(HttpResponse::Ok(), |cursor| {
crate::templates::login(cursor, &login_state)
crate::templates::session::login(cursor, &login_state)
})
.state(&state)
}
@ -140,7 +140,7 @@ async fn login(args: LoginArgs, state: web::Data<State>) -> Result<HttpResponse,
};
rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::login(cursor, &login_state)
crate::templates::session::login(cursor, &login_state)
})
.state(&state)
}
@ -155,7 +155,7 @@ async fn register_page(
};
rendered(HttpResponse::Ok(), |cursor| {
crate::templates::register(cursor, &register_state)
crate::templates::session::register(cursor, &register_state)
})
.state(&state)
}
@ -167,7 +167,7 @@ async fn register(args: RegisterArgs, state: web::Data<State>) -> Result<HttpRes
};
rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::register(cursor, &register_state)
crate::templates::session::register(cursor, &register_state)
})
.state(&state)
}
@ -179,15 +179,15 @@ async fn logout(args: LogoutArgs) -> HttpResponse {
async fn account_page(
uname_args: UpdateUsernamePageArgs,
pass_args: UpdatePasswordPageArgs,
authenticated: Authenticated,
user: User,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let uname_state = hyaenidae_accounts::update_username_page(uname_args);
let pass_state = hyaenidae_accounts::update_password_page(pass_args);
let user = authenticated.user();
rendered(HttpResponse::Ok(), |cursor| {
crate::templates::account(cursor, &user, &uname_state, &pass_state)
crate::templates::session::account(cursor, &user, &uname_state, &pass_state, logout)
})
.state(&state)
}
@ -201,7 +201,8 @@ async fn to_account() -> HttpResponse {
async fn update_username(
uname_args: UpdateUsernameArgs,
pass_args: UpdatePasswordPageArgs,
authenticated: Authenticated,
user: User,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let uname_state = match hyaenidae_accounts::update_username(uname_args)
@ -212,10 +213,9 @@ async fn update_username(
Err(res) => return Ok(res),
};
let pass_state = hyaenidae_accounts::update_password_page(pass_args);
let user = authenticated.user();
rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::account(cursor, &user, &uname_state, &pass_state)
crate::templates::session::account(cursor, &user, &uname_state, &pass_state, logout)
})
.state(&state)
}
@ -223,7 +223,8 @@ async fn update_username(
async fn update_password(
uname_args: UpdateUsernamePageArgs,
pass_args: UpdatePasswordArgs,
authenticated: Authenticated,
user: User,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let uname_state = hyaenidae_accounts::update_username_page(uname_args);
@ -234,16 +235,16 @@ async fn update_password(
Ok(state) => state,
Err(res) => return Ok(res),
};
let user = authenticated.user();
rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::account(cursor, &user, &uname_state, &pass_state)
crate::templates::session::account(cursor, &user, &uname_state, &pass_state, logout)
})
.state(&state)
}
async fn delete_account_page(
args: DeleteUserPageArgs,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let delete_state = match hyaenidae_accounts::delete_user_page(args) {
@ -252,13 +253,14 @@ async fn delete_account_page(
};
rendered(HttpResponse::Ok(), |cursor| {
crate::templates::delete_account(cursor, &delete_state)
crate::templates::session::delete_account(cursor, &delete_state, logout)
})
.state(&state)
}
async fn delete_account(
args: DeleteUserArgs,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let delete_state = match hyaenidae_accounts::delete_user(args).await.state(&state)? {
@ -267,7 +269,7 @@ async fn delete_account(
};
rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::delete_account(cursor, &delete_state)
crate::templates::session::delete_account(cursor, &delete_state, logout)
})
.state(&state)
}

145
server/src/back.rs Normal file
View file

@ -0,0 +1,145 @@
use actix_session::{Session, UserSession};
use actix_web::{
dev::{Payload, Service, ServiceRequest, ServiceResponse, Transform},
FromRequest, HttpRequest, HttpResponse, Responder,
};
use futures_core::future::LocalBoxFuture;
use futures_util::future::{ok, Ready};
use hyaenidae_accounts::AcceptedCookies;
use std::fmt;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Back {
path: String,
}
pub(crate) struct BackPage;
pub(crate) struct BackMiddleware<S>(S);
impl FromRequest for Back {
type Config = ();
type Error = actix_web::Error;
type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let session = req.get_session();
Box::pin(async move {
Ok(Back::data(&session)?.unwrap_or(Back {
path: "/".to_owned(),
}))
})
}
}
impl fmt::Display for Back {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.path, f)
}
}
impl Responder for Back {
type Error = actix_web::Error;
type Future = Ready<Result<HttpResponse, Self::Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(HttpResponse::SeeOther()
.header("Location", self.path)
.finish())
}
}
impl Back {
fn set_data(path: String, session: &Session) -> Result<(), actix_web::Error> {
session.set("back-data", Back { path })?;
Ok(())
}
fn data(session: &Session) -> Result<Option<Self>, actix_web::Error> {
Ok(session.get("back-data")?)
}
pub(crate) fn as_str(&self) -> &str {
&self.path
}
}
impl AsRef<str> for Back {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<S, B> Transform<S> for BackPage
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
S::Future: 'static,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type InitError = ();
type Transform = BackMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(BackMiddleware(service))
}
}
impl<S, B> Service for BackMiddleware<S>
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
S::Future: 'static,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx)
}
fn call(&mut self, req: S::Request) -> Self::Future {
let session = req.get_session();
let path = req
.uri()
.path_and_query()
.map(|paq| paq.as_str())
.unwrap_or("/")
.to_owned();
let (req, pl) = req.into_parts();
let cookie_fut = AcceptedCookies::extract(&req);
let req = ServiceRequest::from_parts(req, pl)
.map_err(|_| ())
.expect("Request has been cloned");
let fut = self.0.call(req);
Box::pin(async move {
if let Err(_) = cookie_fut.await {
return fut.await;
}
match fut.await {
Ok(response) => {
if let Some(content_type) = response.headers().get("Content-Type") {
if let Ok(s) = content_type.to_str() {
if s == "text/html" {
Back::set_data(path, &session)?;
}
}
}
Ok(response)
}
Err(e) => Err(e),
}
})
}
}

View file

@ -5,20 +5,22 @@ use actix_web::{
middleware::{Compress, Logger},
web, App, HttpResponse, HttpServer,
};
use hyaenidae_accounts::{Auth, Authenticated};
use hyaenidae_accounts::{Auth, LogoutState, User};
use sled::Db;
use std::{fmt, time::SystemTime};
use structopt::StructOpt;
mod accounts;
mod apub;
mod back;
mod error;
mod images;
mod jobs;
mod profiles;
use back::{Back, BackPage};
use error::{Error, OptionExt, ResultExt, StateError};
use profiles::CurrentProfile;
use profiles::{CurrentProfile, Profile};
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
@ -87,17 +89,20 @@ async fn main() -> anyhow::Result<()> {
let accounts_state = accounts_state.clone();
App::new()
.wrap(Logger::default())
.wrap(Compress::default())
.wrap(BackPage)
.wrap(CurrentProfile(state.clone()))
.data(state)
.wrap(Auth(accounts_state.clone()))
.data(accounts_state)
.data(SystemTime::now())
.wrap(hyaenidae_accounts::cookie_middlware(&accounts_config))
.wrap(Compress::default())
.wrap(Logger::default())
.route("/", web::get().to(home))
.route("/nav", web::get().to(nav_page))
.route("/404", web::get().to(not_found))
.route("/500", web::get().to(serve_error))
.route("/settings", web::get().to(settings))
.route("/toolkit/{name}", web::get().to(toolkit))
.route("/static/{name}", web::get().to(statics))
.service(accounts::scope())
@ -271,20 +276,29 @@ async fn statics(path: web::Path<String>, startup: web::Data<SystemTime>) -> Htt
async fn home(
state: web::Data<State>,
authenticated: Option<Authenticated>,
logout_args: Option<hyaenidae_accounts::LogoutPageArgs>,
user: Option<User>,
profile: Option<Profile>,
logout: Option<LogoutState>,
) -> Result<HttpResponse, StateError> {
let logout_opt = logout_args.map(|args| hyaenidae_accounts::logout_page(args));
let authenticated_opt = authenticated.and_then(|a| logout_opt.map(|l| (a.user(), l)));
if user.is_some() && profile.is_none() {
return Ok(profiles::to_create());
}
rendered(HttpResponse::Ok(), |cursor| {
templates::index(cursor, authenticated_opt)
templates::index(cursor, &logout, &profile)
})
.state(&state)
}
fn to_404() -> HttpResponse {
HttpResponse::SeeOther().header("Location", "/404").finish()
redirect("/404")
}
fn to_home() -> HttpResponse {
redirect("/")
}
fn redirect(path: &str) -> HttpResponse {
HttpResponse::SeeOther().header("Location", path).finish()
}
async fn not_found(state: web::Data<State>) -> Result<HttpResponse, StateError> {
@ -301,17 +315,42 @@ async fn serve_error(state: web::Data<State>) -> Result<HttpResponse, StateError
.state(&state)
}
async fn nav_page(
logout: Option<LogoutState>,
back: Back,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
rendered(HttpResponse::Ok(), |cursor| {
templates::nav::page(cursor, &logout, &back)
})
.state(&state)
}
async fn settings(
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
rendered(HttpResponse::Ok(), |cursor| {
templates::settings(cursor, logout)
})
.state(&state)
}
fn rendered(
mut builder: HttpResponseBuilder,
f: impl FnOnce(&mut std::io::Cursor<Vec<u8>>) -> std::io::Result<()>,
f: impl FnOnce(&mut std::io::Cursor<&mut Vec<u8>>) -> std::io::Result<()>,
) -> Result<HttpResponse, Error> {
let mut cursor = std::io::Cursor::new(vec![]);
(f)(&mut cursor).map_err(Error::Render)?;
let mut html = cursor.into_inner();
let len = minify_html::in_place(&mut html, &minify_html::Cfg { minify_js: false })?;
html.truncate(len);
let mut bytes = vec![];
(f)(&mut std::io::Cursor::new(&mut bytes)).map_err(Error::Render)?;
minify_html::truncate(
&mut bytes,
&minify_html::Cfg {
minify_js: false,
minify_css: false,
},
)?;
Ok(builder
.content_type(mime::TEXT_HTML.essence_str())
.body(html))
.body(bytes))
}

View file

@ -114,7 +114,7 @@ pub(crate) struct ProfileMiddleware<S>(State, S);
#[derive(Clone)]
struct ProfileExtractor(Rc<RefCell<Option<Profile>>>, Rc<Cell<bool>>, Rc<Event>);
struct ProfileDropGuard(Rc<Event>);
struct ProfileDropGuard(Rc<Event>, Rc<Cell<bool>>);
fn profile_extractor() -> (ProfileExtractor, ProfileDropGuard) {
let event = Rc::new(Event::new());
@ -122,13 +122,14 @@ fn profile_extractor() -> (ProfileExtractor, ProfileDropGuard) {
let flag = Rc::new(Cell::new(false));
(
ProfileExtractor(state, flag, Rc::clone(&event)),
ProfileDropGuard(event),
ProfileExtractor(state, Rc::clone(&flag), Rc::clone(&event)),
ProfileDropGuard(event, flag),
)
}
impl Drop for ProfileDropGuard {
fn drop(&mut self) {
self.1.set(true);
self.0.notify(usize::MAX);
}
}
@ -188,6 +189,7 @@ where
let user_id = if let Ok(auth) = user_fut.await {
auth.user().id()
} else {
drop(drop_guard);
return fut.await;
};
@ -210,8 +212,8 @@ where
}
}
extractor.1.set(true);
drop(drop_guard);
fut.await
})
}

View file

@ -4,7 +4,7 @@ use crate::{
};
use actix_session::Session;
use actix_web::{dev::Payload, web, HttpRequest, HttpResponse, Scope};
use hyaenidae_accounts::Authenticated;
use hyaenidae_accounts::{LogoutState, User};
use uuid::Uuid;
mod middleware;
@ -75,7 +75,7 @@ pub(super) fn scope() -> Scope {
)
}
fn to_create() -> HttpResponse {
pub(super) fn to_create() -> HttpResponse {
redirect("/profiles/create/handle")
}
@ -269,25 +269,27 @@ async fn profiles() -> HttpResponse {
async fn current_profile(
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_profile(profile).await.state(&state)
do_profile(profile, logout).await.state(&state)
}
async fn do_profile(profile: Profile) -> Result<HttpResponse, Error> {
async fn do_profile(profile: Profile, logout: LogoutState) -> Result<HttpResponse, Error> {
let profile_state = ProfileState::new(profile);
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::current(cursor, &profile_state)
crate::templates::profiles::current(cursor, &profile_state, logout)
})
}
async fn update_bio(
form: web::Form<BioForm>,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_update_bio(form.into_inner(), profile, &state)
do_update_bio(form.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -295,6 +297,7 @@ async fn update_bio(
async fn do_update_bio(
form: BioForm,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let display_name = form.display_name.clone();
@ -321,7 +324,7 @@ async fn do_update_bio(
};
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::current(cursor, &state)
crate::templates::profiles::current(cursor, &state, logout)
})
}
@ -329,9 +332,10 @@ async fn update_icon(
request: HttpRequest,
payload: web::Payload,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_update_icon(request, payload.into_inner(), profile, &state)
do_update_icon(request, payload.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -340,6 +344,7 @@ async fn do_update_icon(
request: HttpRequest,
payload: Payload,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let res = state.profiles.upload_image(request, payload).await;
@ -366,7 +371,7 @@ async fn do_update_icon(
state.icon_error(&error);
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::current(cursor, &state)
crate::templates::profiles::current(cursor, &state, logout)
})
}
@ -374,9 +379,10 @@ async fn update_banner(
request: HttpRequest,
payload: web::Payload,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_update_banner(request, payload.into_inner(), profile, &state)
do_update_banner(request, payload.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -385,6 +391,7 @@ async fn do_update_banner(
request: HttpRequest,
payload: Payload,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let res = state.profiles.upload_image(request, payload).await;
@ -411,16 +418,17 @@ async fn do_update_banner(
state.banner_error(&error);
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::current(cursor, &state)
crate::templates::profiles::current(cursor, &state, logout)
})
}
async fn update_require_login(
form: web::Form<RequireLoginForm>,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_update_require_login(form.into_inner(), profile, &state)
do_update_require_login(form.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -428,6 +436,7 @@ async fn update_require_login(
async fn do_update_require_login(
form: RequireLoginForm,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let login_required = form.require_login.is_some();
@ -452,7 +461,7 @@ async fn do_update_require_login(
};
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::current(cursor, &state)
crate::templates::profiles::current(cursor, &state, logout)
})
}
@ -462,15 +471,20 @@ struct ChangeProfileForm {
}
async fn change_profile_page(
auth: Authenticated,
user: User,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let user_id = auth.user().id();
do_change_profile_page(user_id, &state).await.state(&state)
do_change_profile_page(user.id(), logout, &state)
.await
.state(&state)
}
async fn do_change_profile_page(user_id: Uuid, state: &State) -> Result<HttpResponse, Error> {
async fn do_change_profile_page(
user_id: Uuid,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let profiles = state
.profiles
.store
@ -480,18 +494,17 @@ async fn do_change_profile_page(user_id: Uuid, state: &State) -> Result<HttpResp
.collect::<Vec<_>>();
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::list(cursor, &profiles)
crate::templates::profiles::list(cursor, &profiles, logout)
})
}
async fn change_profile(
auth: Authenticated,
user: User,
session: Session,
form: web::Form<ChangeProfileForm>,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let user_id = auth.user().id();
do_change_profile(user_id, session, form.into_inner(), &state)
do_change_profile(user.id(), session, form.into_inner(), &state)
.await
.state(&state)
}
@ -511,7 +524,7 @@ async fn do_change_profile(
.ok()
.req()?;
Ok(to_current_profile())
Ok(crate::to_home())
}
#[derive(Clone, Debug, serde::Deserialize)]
@ -519,34 +532,39 @@ pub struct HandleForm {
pub(crate) handle: String,
}
async fn new_handle(_: Authenticated, state: web::Data<State>) -> Result<HttpResponse, StateError> {
async fn new_handle(
_: User,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
let mut handle_input = hyaenidae_toolkit::TextInput::new("handle");
handle_input.placeholder("Handle").title("Handle");
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::create::handle(cursor, &handle_input)
crate::templates::profiles::create::handle(cursor, &handle_input, logout)
})
.state(&state)
}
async fn create_handle(
auth: Authenticated,
user: User,
form: web::Form<HandleForm>,
session: Session,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_create_handle(auth, form.into_inner(), session, &state)
do_create_handle(user.id(), form.into_inner(), session, logout, &state)
.await
.state(&state)
}
async fn do_create_handle(
auth: Authenticated,
user_id: Uuid,
form: HandleForm,
session: Session,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let user_id = auth.user().id();
let domain = state.domain.clone();
let handle = form.handle.clone();
@ -585,7 +603,7 @@ async fn do_create_handle(
.error_opt(error);
crate::rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::profiles::create::handle(cursor, &handle_input)
crate::templates::profiles::create::handle(cursor, &handle_input, logout)
})
}
@ -595,11 +613,15 @@ pub struct BioForm {
pub(crate) description: String,
}
async fn new_bio(profile: Profile, state: web::Data<State>) -> Result<HttpResponse, StateError> {
do_new_bio(profile).await.state(&state)
async fn new_bio(
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_new_bio(profile, logout).await.state(&state)
}
async fn do_new_bio(profile: Profile) -> Result<HttpResponse, Error> {
async fn do_new_bio(profile: Profile, logout: LogoutState) -> Result<HttpResponse, Error> {
let mut display_name = hyaenidae_toolkit::TextInput::new("display_name");
display_name
.title("Display Name")
@ -618,16 +640,23 @@ async fn do_new_bio(profile: Profile) -> Result<HttpResponse, Error> {
}
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::create::bio(cursor, &display_name, &description, &profile)
crate::templates::profiles::create::bio(
cursor,
&display_name,
&description,
&profile,
logout,
)
})
}
async fn create_bio(
form: web::Form<BioForm>,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_create_bio(form.into_inner(), profile, &state)
do_create_bio(form.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -635,6 +664,7 @@ async fn create_bio(
async fn do_create_bio(
form: BioForm,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let display_name = form.display_name.clone();
@ -671,21 +701,31 @@ async fn do_create_bio(
let profile = profile.refresh(state)?;
crate::rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::profiles::create::bio(cursor, &display_name, &description, &profile)
crate::templates::profiles::create::bio(
cursor,
&display_name,
&description,
&profile,
logout,
)
})
}
async fn new_icon(profile: Profile, state: web::Data<State>) -> Result<HttpResponse, StateError> {
do_new_icon(profile).await.state(&state)
async fn new_icon(
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_new_icon(profile, logout).await.state(&state)
}
async fn do_new_icon(profile: Profile) -> Result<HttpResponse, Error> {
async fn do_new_icon(profile: Profile, logout: LogoutState) -> Result<HttpResponse, Error> {
let mut icon_input =
hyaenidae_toolkit::FileInput::secondary("images[]", "Select Icon", "icon-input");
icon_input.accept(ACCEPT_TYPES);
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::create::icon(cursor, &icon_input, None, &profile)
crate::templates::profiles::create::icon(cursor, &icon_input, None, &profile, logout)
})
}
@ -693,9 +733,10 @@ async fn create_icon(
request: HttpRequest,
payload: web::Payload,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_create_icon(request, payload.into_inner(), profile, &state)
do_create_icon(request, payload.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -704,6 +745,7 @@ async fn do_create_icon(
request: HttpRequest,
payload: Payload,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let res = state.profiles.upload_image(request, payload).await;
@ -732,21 +774,25 @@ async fn do_create_icon(
let profile = profile.refresh(state)?;
crate::rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::profiles::create::icon(cursor, &icon_input, error, &profile)
crate::templates::profiles::create::icon(cursor, &icon_input, error, &profile, logout)
})
}
async fn new_banner(profile: Profile, state: web::Data<State>) -> Result<HttpResponse, StateError> {
do_new_banner(profile).await.state(&state)
async fn new_banner(
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_new_banner(profile, logout).await.state(&state)
}
async fn do_new_banner(profile: Profile) -> Result<HttpResponse, Error> {
async fn do_new_banner(profile: Profile, logout: LogoutState) -> Result<HttpResponse, Error> {
let mut banner_input =
hyaenidae_toolkit::FileInput::secondary("images[]", "Select Banner", "banner-input");
banner_input.accept(ACCEPT_TYPES);
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::create::banner(cursor, &banner_input, None, &profile)
crate::templates::profiles::create::banner(cursor, &banner_input, None, &profile, logout)
})
}
@ -754,9 +800,10 @@ async fn create_banner(
request: HttpRequest,
payload: web::Payload,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_create_banner(request, payload.into_inner(), profile, &state)
do_create_banner(request, payload.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -765,6 +812,7 @@ async fn do_create_banner(
request: HttpRequest,
payload: Payload,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
let res = state.profiles.upload_image(request, payload).await;
@ -793,7 +841,7 @@ async fn do_create_banner(
let profile = profile.refresh(state)?;
crate::rendered(HttpResponse::BadRequest(), |cursor| {
crate::templates::profiles::create::banner(cursor, &banner_input, error, &profile)
crate::templates::profiles::create::banner(cursor, &banner_input, error, &profile, logout)
})
}
@ -804,18 +852,23 @@ struct RequireLoginForm {
async fn new_require_login(
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_new_require_login(profile).await.state(&state)
do_new_require_login(profile, logout).await.state(&state)
}
async fn do_new_require_login(profile: Profile) -> Result<HttpResponse, Error> {
async fn do_new_require_login(
profile: Profile,
logout: LogoutState,
) -> Result<HttpResponse, Error> {
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::create::require_login(
cursor,
profile.inner.login_required(),
None,
&profile,
logout,
)
})
}
@ -823,9 +876,10 @@ async fn do_new_require_login(profile: Profile) -> Result<HttpResponse, Error> {
async fn create_require_login(
form: web::Form<RequireLoginForm>,
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_create_require_login(form.into_inner(), profile, &state)
do_create_require_login(form.into_inner(), profile, logout, &state)
.await
.state(&state)
}
@ -833,6 +887,7 @@ async fn create_require_login(
async fn do_create_require_login(
form: RequireLoginForm,
profile: Profile,
logout: LogoutState,
state: &State,
) -> Result<HttpResponse, Error> {
use hyaenidae_profiles::apub::actions::UpdateProfile;
@ -857,16 +912,21 @@ async fn do_create_require_login(
form.require_login.is_some(),
error,
&profile,
logout,
)
})
}
async fn done(profile: Profile, state: web::Data<State>) -> Result<HttpResponse, StateError> {
do_done(profile).await.state(&state)
async fn done(
profile: Profile,
logout: LogoutState,
state: web::Data<State>,
) -> Result<HttpResponse, StateError> {
do_done(profile, logout).await.state(&state)
}
async fn do_done(profile: Profile) -> Result<HttpResponse, Error> {
async fn do_done(profile: Profile, logout: LogoutState) -> Result<HttpResponse, Error> {
crate::rendered(HttpResponse::Ok(), |cursor| {
crate::templates::profiles::create::done(cursor, &profile)
crate::templates::profiles::create::done(cursor, &profile, logout)
})
}

View file

@ -1,20 +0,0 @@
@use super::layout;
@use hyaenidae_accounts::{templates::{update_password, update_username}, UpdatePasswordState, UpdateUsernameState, User};
@use hyaenidae_toolkit::{templates::{button, card, card_body, link}, Button, Card, Link};
@(user: &User, uname_state: &UpdateUsernameState, pass_state: &UpdatePasswordState)
@:layout(&format!("Account Settings for {}", user.username()), "Update account information", {}, {
@:update_username(Card::full_width().classes(&["account-page"]), uname_state)
@:update_password(Card::full_width().classes(&["account-page"]), pass_state)
@:card(Card::full_width().classes(&["account-page"]), { Danger }, {
@:card_body({
@:button(Button::primary("Delete Account").href("/session/account/delete"))
})
})
@:card(Card::full_width().classes(&["account-page"]), { Nav }, {
@:card_body({
@:link(&Link::current_tab("/"), { Return Home })
})
})
})

View file

@ -1,9 +0,0 @@
@use super::layout;
@use hyaenidae_accounts::{templates::delete_user, DeleteUserState};
@use hyaenidae_toolkit::Card;
@(state: &DeleteUserState)
@:layout("Delete Account", "Are you sure you want to delete your account?", {}, {
@:delete_user(&Card::full_width(), state)
})

View file

@ -1,12 +1,11 @@
@use super::layout;
@use hyaenidae_toolkit::{templates::{card, card_body, link}, Card, Link};
@use crate::templates::layouts::main;
@use hyaenidae_toolkit::{templates::{card, card_body, card_title, link}, Card, Link};
@(error: String)
@:layout("Error", "There was an error processing your request", {}, {
@:main("Error", "There was an error processing your request", {}, {
@:card(&Card::full_width(), {
There was an error processing your request
}, {
@:card_title({ There was an error processing your request })
@:card_body({ @error })
@:card_body({
@:link(&Link::current_tab("/"), { Return Home })

View file

@ -1,28 +1,22 @@
@use super::layout;
@use hyaenidae_accounts::{LogoutState, User};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, link}, Button, Card, Link};
@use crate::{profiles::Profile, templates::layouts::home};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{card, card_body, card_title}, Card};
@(logout_opt: Option<(User, LogoutState)>)
@(logout_opt: &Option<LogoutState>, profile: &Option<Profile>)
@:layout("Hyaenidae", "A simple website", {}, {
@if let Some((user, logout_state)) = logout_opt {
@:card(&Card::full_width(), { Welcome, @user.username() }, {
@:home("Hyaenidae", "A simple website", logout_opt, {}, {
@if let Some(profile) = profile {
@:card(&Card::full_width(), {
@:card_title({ Welcome, @profile.name() })
@:card_body({
@:link(&Link::current_tab("/session/account"), { Account Settings })
})
@:card_body({
@:button_group(&[
Button::primary_outline("Profile").href("/profiles"),
logout_state.button(&Button::primary_outline("Logout"))
])
<span>e</span>
})
})
} else {
@:card(&Card::full_width(), { Home... }, {
@:card(&Card::full_width(), {
@:card_title({ Home })
@:card_body({
@:button_group(&[
Button::primary_outline("Login").href("/session/auth/login")
])
<span>e</span>
})
})
}

View file

@ -0,0 +1,26 @@
@use crate::templates::{layouts::root, nav::nav};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{centered, link}, Link};
@(title: &str, description: &str, logout_opt: &Option<LogoutState>, head: Content, body: Content)
@:root(title, description, { @:head() }, {
<div class="top-bar">
<div class="top-bar--left">
<h2>@title</h2>
</div>
<div class="top-bar--right">
@:nav(logout_opt)
</div>
</div>
<div class="top-bar__mobile">
<h2>@title</h2>
<h3>@:link(&Link::current_tab("/nav"), { Nav })</h3>
</div>
<div class="home-content">
@:centered(false, {
@:body()
})
</div>
})

View file

@ -0,0 +1,14 @@
@use crate::templates::layouts::root;
@use hyaenidae_toolkit::templates::centered;
@(title: &str, description: &str, head: Content, body: Content)
@:root(title, description, { @:head() }, {
@:centered(true, {
<h2 class="title">@title</h2>
<p class="description">@description</p>
})
@:centered(false, {
@:body()
})
})

View file

@ -1,5 +1,5 @@
@use crate::{toolkit_path, statics_path, templates::statics::layout_css};
@use hyaenidae_toolkit::templates::{centered, statics::toolkit_css};
@use hyaenidae_toolkit::templates::statics::toolkit_css;
@(title: &str, description: &str, head: Content, body: Content)
@ -18,12 +18,6 @@
@:head()
</head>
<body>
@:centered(true, {
<h2 class="title">@title</h2>
<p class="description">@description</p>
})
@:centered(false, {
@:body()
})
@:body()
</body>
</html>

View file

@ -1,9 +0,0 @@
@use super::layout;
@use hyaenidae_accounts::{templates::login, LoginState};
@use hyaenidae_toolkit::Card;
@(login_state: &LoginState)
@:layout("Login", "Log into Hyaenidae", {}, {
@:login(&Card::full_width(), login_state)
})

View file

@ -0,0 +1,18 @@
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::button_group, Button};
@(user_opt: &Option<LogoutState>)
@if let Some(logout_state) = user_opt.as_ref() {
@:button_group(&[
Button::primary("Home").href("/"),
Button::secondary("Settings").href("/settings"),
Button::secondary("Switch Profile").href("/profiles/change"),
logout_state.button(&Button::primary_outline("Logout"))
])
} else {
@:button_group(&[
Button::primary_outline("Login").href("/session/auth/login"),
Button::primary_outline("Register").href("/session/auth/register"),
])
}

View file

@ -0,0 +1,18 @@
@use crate::{templates::{layouts::root, nav::nav}, Back};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, centered}, Button, Card};
@(user_opt: &Option<LogoutState>, back: &Back)
@:root("Navigation", "Links to get around", {}, {
@:centered(false, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_body({
@:nav(user_opt)
})
@:card_body({
@:button_group(&[Button::primary_outline("Back").href(back.as_str())])
})
})
})
})

View file

@ -1,10 +1,11 @@
@use super::layout;
@use hyaenidae_toolkit::{templates::{card, card_body, link}, Card, Link};
@use crate::templates::layouts::main;
@use hyaenidae_toolkit::{templates::{card, card_body, card_title, link}, Card, Link};
@()
@:layout("404", "Not Found", {}, {
@:card(&Card::full_width(), { We couldn't find that }, {
@:main("404", "Not Found", {}, {
@:card(&Card::full_width(), {
@:card_title({ We couldn't find that })
@:card_body({
@:link(&Link::current_tab("/"), { Return Home })
})

View file

@ -1,11 +1,16 @@
@use crate::{templates::{layout, profiles::view}, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, file_input}, Button, Card, FileInput};
@use crate::{templates::{layouts::home, profiles::view}, profiles::Profile};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input, statics::{button_js, file_input_js}}, Button, Card, FileInput};
@(banner_input: &FileInput, error: Option<String>, profile: &Profile)
@(banner_input: &FileInput, error: Option<String>, profile: &Profile, logout: LogoutState)
@:layout("Create Profile", "Create a new profile on Hyaenidae", {}, {
@:card(&Card::full_width().classes(&["account-page"]), { Add a banner }, {
@:home("Create Profile", "Create a new profile on Hyaenidae", &Some(logout), {
<script src="/toolkit/@file_input_js.name"></script>
<script src="/toolkit/@button_js.name"></script>
}, {
@:card(&Card::full_width().classes(&["account-page"]), {
<form method="POST" action="/profiles/create/banner" enctype="multipart/form-data">
@:card_title({ Add a banner })
@:card_body({
<p>
This banner will be displayed on your profile behind your icon.
@ -29,7 +34,8 @@
})
</form>
})
@:card(&Card::full_width().classes(&["account-page"]), { Preview }, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Preview })
@:view("", profile)
})
})

View file

@ -1,11 +1,15 @@
@use crate::{templates::{layout, profiles::view}, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, text_input}, Button, Card, TextInput};
@use crate::{templates::{layouts::home, profiles::view}, profiles::Profile};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, text_input, statics::button_js}, Button, Card, TextInput};
@(display_name_input: &TextInput, description_input: &TextInput, profile: &Profile)
@(display_name_input: &TextInput, description_input: &TextInput, profile: &Profile, logout: LogoutState)
@:layout("Create Profile", "Create a new profile on Hyaenidae", {}, {
@:card(&Card::full_width().classes(&["account-page"]), { Create a Bio }, {
@:home("Create Profile", "Create a new profile on Hyaenidae", &Some(logout), {
<script src="/toolkit/@button_js.name"></script>
}, {
@:card(&Card::full_width().classes(&["account-page"]), {
<form method="POST" action="/profiles/create/bio">
@:card_title({ Create a Bio })
@:card_body({
<p>
This is where you can talk a bit about yourself. the Display Name is the name
@ -25,7 +29,8 @@
})
</form>
})
@:card(&Card::full_width().classes(&["account-page"]), { Preview }, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Preview })
@:view("", profile)
})
})

View file

@ -1,10 +1,12 @@
@use crate::{templates::{layout, profiles::view}, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body}, Button, Card};
@use crate::{templates::{layouts::home, profiles::view}, profiles::Profile};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title}, Button, Card};
@(profile: &Profile)
@(profile: &Profile, logout: LogoutState)
@:layout("Create Profile", "Create a new profile on Hyaenidae", {}, {
@:card(&Card::full_width().classes(&["account-page"]), { Finished! }, {
@:home("Create Profile", "Create a new profile on Hyaenidae", &Some(logout), {}, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Finished! })
@:card_body({
<p>
Congratulations! You're profile is all set up! Now you can start to browse
@ -22,7 +24,8 @@
])
})
})
@:card(&Card::full_width().classes(&["account-page"]), { Preview }, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Preview })
@:view("", profile)
})
})

View file

@ -1,11 +1,15 @@
@use crate::templates::layout;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, text_input}, Button, Card, TextInput};
@use crate::templates::layouts::home;
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, text_input, statics::button_js}, Button, Card, TextInput};
@(handle_input: &TextInput)
@(handle_input: &TextInput, logout: LogoutState)
@:layout("Create Profile", "Create a new profile on Hyaenidae", {}, {
@:card(&Card::full_width(), { Create a Handle }, {
@:home("Create Profile", "Create a new profile on Hyaenidae", &Some(logout), {
<script src="/toolkit/@button_js.name"></script>
}, {
@:card(&Card::full_width().classes(&["account-page"]), {
<form method="POST" action="/profiles/create/handle">
@:card_title({ Create a Handle })
@:card_body({
<p>
A handle is how people on Hyaenidae will find your account. You can make this the

View file

@ -1,11 +1,16 @@
@use crate::{templates::{layout, profiles::view}, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, file_input}, Button, Card, FileInput};
@use crate::{templates::{layouts::home, profiles::view}, profiles::Profile};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input, statics::{button_js, file_input_js}}, Button, Card, FileInput};
@(icon_input: &FileInput, error: Option<String>, profile: &Profile)
@(icon_input: &FileInput, error: Option<String>, profile: &Profile, logout: LogoutState)
@:layout("Create Profile", "Create a new profile on Hyaenidae", {}, {
@:card(&Card::full_width().classes(&["account-page"]), { Add an Icon }, {
@:home("Create Profile", "Create a new profile on Hyaenidae", &Some(logout), {
<script src="/toolkit/@file_input_js.name"></script>
<script src="/toolkit/@button_js.name"></script>
}, {
@:card(&Card::full_width().classes(&["account-page"]), {
<form method="POST" action="/profiles/create/icon" enctype="multipart/form-data">
@:card_title({ Add an Icon })
@:card_body({
<p>
This icon will be displayed on your profile, and next to submissions
@ -30,7 +35,8 @@
})
</form>
})
@:card(&Card::full_width().classes(&["account-page"]), { Preview }, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Preview })
@:view("", profile)
})
})

View file

@ -1,11 +1,15 @@
@use crate::{templates::{layout, profiles::view}, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body}, Button, Card};
@use crate::{templates::{layouts::home, profiles::view}, profiles::Profile};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, statics::button_js}, Button, Card};
@(require_login: bool, error: Option<String>, profile: &Profile)
@(require_login: bool, error: Option<String>, profile: &Profile, logout: LogoutState)
@:layout("Create Profile", "Create a new profile on Hyaenidae", {}, {
@:card(&Card::full_width().classes(&["account-page"]), { Require Login }, {
@:home("Create Profile", "Create a new profile on Hyaenidae", &Some(logout), {
<script src="/toolkit/@button_js.name"></script>
}, {
@:card(&Card::full_width().classes(&["account-page"]), {
<form method="POST" action="/profiles/create/require-login">
@:card_title({ Require Login })
@:card_body({
<p>
If you would like to hide your profile from people without accounts, you
@ -43,7 +47,8 @@
})
</form>
})
@:card(&Card::full_width().classes(&["account-page"]), { Preview }, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Preview })
@:view("", profile)
})
})

View file

@ -1,11 +1,16 @@
@use crate::{templates::{layout, profiles::{banner, icon, view}}, profiles::ProfileState};
@use hyaenidae_toolkit::{templates::{button, button_group, card, card_body, file_input, text_input}, Button, Card};
@use crate::{templates::{layouts::home, profiles::{banner, icon, view}}, profiles::ProfileState};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button, button_group, card, card_body, card_title, file_input, text_input, statics::{button_js, file_input_js}}, Button, Card};
@(state: &ProfileState)
@(state: &ProfileState, logout: LogoutState)
@:layout("Profile Settings", &format!("{}'s profile", state.profile.name()), {}, {
@:home("Profile Settings", &format!("{}'s profile", state.profile.name()), &Some(logout), {
<script src="/toolkit/@file_input_js.name"></script>
<script src="/toolkit/@button_js.name"></script>
}, {
@:view("standalone account-page", &state.profile)
@:card(&Card::full_width().classes(&["account-page"]), { Update Profile }, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Update Profile })
@:card_body({
<form method="POST" action="/profiles/update/bio">
<div class="columns">
@ -131,10 +136,4 @@
</form>
})
})
@:card(&Card::full_width().classes(&["account-page"]), {
@:button_group(&[
Button::primary_outline("Create New Profile").href("/profiles/create/handle"),
Button::primary_outline("Switch Profile").href("/profiles/change"),
])
}, {})
})

View file

@ -1,27 +1,27 @@
@use crate::{templates::{layout, profiles::view}, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body}, Button, Card};
@use crate::{templates::{layouts::home, profiles::view}, profiles::Profile};
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title}, Button, Card};
@(profiles: &[Profile])
@(profiles: &[Profile], logout: LogoutState)
@:layout("Switch Profile", "Select the profile you wish to use", {}, {
@:card(&Card::full_width().classes(&["account-page"]), { Create New Profile }, {
@:home("Switch Profile", "Select the profile you wish to use", &Some(logout), {}, {
@for profile in profiles {
@:card(&Card::full_width().classes(&["account-page"]), {
@:view("card-top", profile)
@:card_body({
<form method="POST" action="/profiles/change">
<input type="hidden" name="profile_id" value="@profile.id()" />
@:button_group(&[&Button::primary(&format!("Select {}", profile.name()))])
</form>
})
})
}
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Create a New Profile })
@:card_body({
@:button_group(&[
Button::primary_outline("Create").href("/profiles/create/handle"),
])
})
})
@for profile in profiles {
@:card(&Card::full_width().classes(&["account-page"]), {
@profile.name()
}, {
@:view("", profile)
@:card_body({
<form method="POST" action="/profiles/change">
<input type="hidden" name="profile_id" value="@profile.id()" />
@:button_group(&[&Button::primary("Select")])
</form>
})
})
}
})

View file

@ -1,9 +0,0 @@
@use super::layout;
@use hyaenidae_accounts::{templates::register, RegisterState};
@use hyaenidae_toolkit::Card;
@(register_state: &RegisterState)
@:layout("Register", "Register for Hyaenidae", {}, {
@:register(&Card::full_width(), register_state)
})

View file

@ -0,0 +1,23 @@
@use crate::templates::layouts::home;
@use hyaenidae_accounts::{templates::{update_password, update_username}, LogoutState, UpdatePasswordState, UpdateUsernameState, User};
@use hyaenidae_toolkit::{templates::{button, card, card_body, card_title, link, statics::button_js}, Button, Card, Link};
@(user: &User, uname_state: &UpdateUsernameState, pass_state: &UpdatePasswordState, logout: LogoutState)
@:home(&format!("Account Settings for {}", user.username()), "Update account information", &Some(logout), {
<script src="/toolkit/@button_js.name"></script>
}, {
@:update_username(Card::full_width().classes(&["account-page"]), uname_state)
@:update_password(Card::full_width().classes(&["account-page"]), pass_state)
@:card(Card::full_width().classes(&["account-page"]), {
@:card_title({ Danger })
@:card_body({
@:button(Button::primary("Delete Account").href("/session/account/delete"))
})
})
@:card(Card::full_width().classes(&["account-page"]), {
@:card_body({
@:link(&Link::current_tab("/"), { Return Home })
})
})
})

View file

@ -1,10 +1,10 @@
@use super::layout;
@use crate::templates::layouts::home;
@use hyaenidae_accounts::{templates::cookies, CookiesState};
@use hyaenidae_toolkit::Card;
@(cookie_state: &CookiesState)
@:layout("Accept Cookies", "Review the cookie policy", {}, {
@:home("Accept Cookies", "Review the cookie policy", &None, {}, {
@:cookies(&Card::full_width(), cookie_state)
})

View file

@ -0,0 +1,9 @@
@use crate::templates::layouts::home;
@use hyaenidae_accounts::{templates::delete_user, DeleteUserState, LogoutState};
@use hyaenidae_toolkit::Card;
@(state: &DeleteUserState, logout: LogoutState)
@:home("Delete Account", "Are you sure you want to delete your account?", &Some(logout), {}, {
@:delete_user(&Card::full_width(), state)
})

View file

@ -0,0 +1,11 @@
@use crate::templates::layouts::home;
@use hyaenidae_accounts::{templates::login, LoginState};
@use hyaenidae_toolkit::{templates::statics::button_js, Card};
@(login_state: &LoginState)
@:home("Login", "Log into Hyaenidae", &None, {
<script src="/toolkit/@button_js.name"></script>
}, {
@:login(&Card::full_width(), login_state)
})

View file

@ -0,0 +1,11 @@
@use crate::templates::layouts::home;
@use hyaenidae_accounts::{templates::register, RegisterState};
@use hyaenidae_toolkit::{templates::statics::button_js, Card};
@(register_state: &RegisterState)
@:home("Register", "Register for Hyaenidae", &None, {
<script src="/toolkit/@button_js.name"></script>
}, {
@:register(&Card::full_width(), register_state)
})

View file

@ -0,0 +1,30 @@
@use crate::templates::layouts::home;
@use hyaenidae_accounts::LogoutState;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, link}, Button, Card, Link};
@(logout: LogoutState)
@:home("Settings", "Update settings", &Some(logout.clone()), {}, {
@:card(&Card::full_width().classes(&["account-page"]), {
@:card_title({ Settings })
@:card_body({
<ul>
<li>
@:link(&Link::current_tab("/profiles"), {
Profile Settings
})
</li>
<li>
@:link(&Link::current_tab("/session/account"), {
Account Settings
})
</li>
</ul>
})
@:card_body({
@:button_group(&[
logout.button(&Button::primary_outline("Logout"))
])
})
})
})