~lthms/ogmios

04626ceb0c598709c67700277346313e42810666 — Thomas Letan 1 year, 8 months ago 5d8d26b
refactor: Introduce helper functions to render ogmios templates

The previous situation was not acceptable, as many code was duplicated
across route handlers regarding the template generation. For instance,
each route has a call to retrieve the list of validated characters of
a given user.

Now, with shared::render_noauth_template, shared::render_auth_template
and shared::render_template, the situation is a bit better. We
probably will end up in situations where we are doing useless
requests, e.g. because we already fetched the list of characters. For
now, I consider this is OKay.
M Cargo.lock => Cargo.lock +1 -0
@@ 428,6 428,7 @@ dependencies = [
 "rocket_contrib 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
 "serde 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
 "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
 "serde_json 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]

M Cargo.toml => Cargo.toml +1 -0
@@ 12,6 12,7 @@ rocket_codegen    = "0.3"
maud              = "0.18"
serde             = "1.0"
serde_derive      = "1.0"
serde_json        = "1.0"
base64            = "0.9"

[dependencies.diesel]

M src/main.rs => src/main.rs +24 -35
@@ 15,6 15,7 @@
#[macro_use] extern crate error_chain;
#[macro_use] extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate pretty_env_logger;
#[macro_use] extern crate log;
extern crate rocket;


@@ 41,7 42,9 @@ pub mod routes;

use db::{PgPool, PgConn, establish_connection};

use ::schema::users;
use ::errors::{Error, ResultExt};
use ::routes::shared;
use ::models::user::User;
use ::models::sheet::Sheet;
use ::models::image::{ImageId, Image};


@@ 50,57 53,43 @@ use ::models::image::{ImageId, Image};
fn index_auth(
    conn: PgConn,
    msg: Option<FlashMessage>,
    mut user: User,
    user: User,
) -> Result<Template, Error> {
    use schema::users::dsl::*;

    let msg = msg.map(|msg| json!({
        "name": msg.name(),
        "msg":  msg.msg()
    }));

    let nb = users
    let nb = users::table
        .count()
        .get_result::<i64>(conn.get())
        .chain_err(|| "Could not query users table")?;

    let sheets = Sheet::get_current_sheets_of(user.id, &conn)?;

    Ok(Template::render("index", json!({
        "msg": msg,
        "nb": nb,
        "user": json!({
            "default": user.default_character,
            "characters": sheets,
            "nickname": user.nickname
    shared::render_auth_template(
        user,
        msg,
        "index",
        json!({
            "nb": nb,
        }),
        "hash": env!("GIT_HASH")
    })))
        &conn
    )
}

#[get("/", rank=2)]
fn index_noauth(
    pool: State<PgPool>,
    conn: PgConn,
    msg: Option<FlashMessage>
) -> Result<Template, Error> {
    use schema::users::dsl::*;
    let p = pool.get().chain_err(|| "Database not availabde")?;

    let msg = msg.map(|msg| json!({
        "name": msg.name(),
        "msg":  msg.msg()
    }));

    let nb = users
    let nb = users::table
        .count()
        .get_result::<i64>(&p)
        .get_result::<i64>(conn.get())
        .chain_err(|| "Could not query users table")?;

    Ok(Template::render("index", json!({
        "msg": msg,
        "nb": nb,
        "hash": env!("GIT_HASH")
    })))
    shared::render_noauth_template(
        msg,
        "index",
        json!({
            "nb": nb,
        }),
        &conn
    )
}

#[get("/img/<id>")]

M src/routes/account/mod.rs => src/routes/account/mod.rs +9 -9
@@ 1,29 1,29 @@
use rocket_contrib::Template;
use rocket::request::FlashMessage;

use ::db::PgConn;
use ::errors::Error;
use ::models::user::User;
use ::models::sheet::Sheet;
use ::routes::shared;

#[get("/account")]
pub fn account_index(
    msg: Option<FlashMessage>,
    user: User,
    conn: PgConn,
) -> Result<Template, Error> {
    let currents = Sheet::get_current_sheets_of(user.id, &conn)?;
    let futures = Sheet::get_future_sheets_of(user.id, &conn)?;

    Ok(Template::render(
    shared::render_auth_template(
        user,
        msg,
        "account_index",
        json!({
            "hash": env!("GIT_HASH"),
            "currents": currents,
            "futures": futures,
            "user": json!({
                "default": user.default_character,
                "characters": currents,
                "nickname": user.nickname
            }),
        })
    ))
        }),
        &conn
    )
}

M src/routes/characters.rs => src/routes/characters.rs +25 -53
@@ 9,6 9,7 @@ use ::models::user::User;
use ::models::sheet::{Sheet, SheetExtended, Content};
use ::models::document::Document;
use ::models::id::CharacterId;
use ::routes::shared;
use ::routes::document::RenderRequest;

#[get("/character/<id>/current")]


@@ 18,11 19,6 @@ pub fn get_current_sheet(
    id: i32,
    msg: Option<FlashMessage>,
) -> Result<Template, Error> {
    let msg = msg.map(|msg| json!({
        "name": msg.name(),
        "msg":  msg.msg()
    }));

    let id = CharacterId::from(id);

    let sheet = match Sheet::get_current_sheet(id, &conn)? {


@@ 36,35 32,21 @@ pub fn get_current_sheet(

    let future = Sheet::get_future_sheet(id, &conn)?.is_some();

    let user = match user {
        Some(user) => {
            let sheets = Sheet::get_current_sheets_of(user.id, &conn)?;

            Some(json!({
                "default": user.default_character,
                "characters": sheets,
                "nickname": user.nickname
            }))
        }
        _ => {
            None
        }
    };

    Ok(Template::render(
    shared::render_template(
        user,
        msg,
        "character_current",
        json!({
            "msg": msg,
            "sheet": sheet,
            "future": future,
            "hash": env!("GIT_HASH"),
            "user": user,
        })
    ))
        }),
        &conn
    )
}

#[get("/character/<id>/future")]
pub fn get_future_sheet(
    msg: Option<FlashMessage>,
    user: User,
    conn: PgConn,
    id: i32,


@@ 81,21 63,17 @@ pub fn get_future_sheet(
    };

    let current = Sheet::get_current_sheet(id, &conn)?.is_some();
    let sheets = Sheet::get_current_sheets_of(user.id, &conn)?;

    Ok(Template::render(
    shared::render_auth_template(
        user,
        msg,
        "character_future",
        json!({
            "sheet": sheet,
            "current": current,
            "hash": env!("GIT_HASH"),
            "user": json!({
                "default": user.default_character,
                "characters": sheets,
                "nickname": user.nickname
            }),
        })
    ))
        }),
        &conn
    )
}

#[post("/character/<id>/future/validate")]


@@ 114,7 92,7 @@ pub fn validate_future_sheet(

#[post("/character/<id>/future/new")]
pub fn new_future_sheet(
    user: User,
    _user: User,
    conn: PgConn,
    id: i32,
) -> Result<Flash<Redirect>, Error> {


@@ 147,31 125,25 @@ pub fn edit_future_sheet(
) -> Result<Template, Error> {
    let id = CharacterId::from(id);

    let sheets = Sheet::get_current_sheets_of(user.id, &conn)?;
    let sheet = Sheet::get_future_sheet(id.into(), &conn)?
        .ok_or("Character does not have a sheet to edit".into(): Error)
        .and_then(|x| SheetExtended::extend_from(x, &conn))?;

    Ok(
        Template::render(
            "character_edit",
            json!({
                "target": format!("/character/{}/future/edit", id.into(): i32),
                "hash": env!("GIT_HASH"),
                "previous": sheet,
                "user": json!({
                    "default": user.default_character,
                    "characters": sheets,
                    "nickname": user.nickname
                }),
            })
        )
    shared::render_auth_template(
        user,
        None,
        "character_edit",
        json!({
            "target": format!("/character/{}/future/edit", id.into(): i32),
            "previous": sheet,
        }),
        &conn
    )
}

#[post("/character/<id>/future/edit", data="<submission>")]
pub fn edit_future_sheet_post(
    user: User,
    _user: User,
    submission: Form<RenderRequest>,
    conn: PgConn,
    id: i32,

M src/routes/document.rs => src/routes/document.rs +9 -8
@@ 9,6 9,7 @@ use ::models::sheet::Sheet;
use ::models::document::{Lang, Document};
use ::models::sheet::Content;
use ::errors::Error;
use ::routes::shared;

#[get("/document")]
pub fn submit_document(


@@ 18,15 19,15 @@ pub fn submit_document(

    let sheets = Sheet::get_current_sheets_of(user.id, &conn)?;

    Ok(Template::render("document", json!({
        "user": json!({
            "default": user.default_character,
            "characters": sheets,
            "nickname": user.nickname
    shared::render_auth_template(
        user,
        None,
        "document",
        json!({
            "target": "/document",
        }),
        "target": "/document",
        "hash": env!("GIT_HASH")
    })))
        &conn
    )
}

#[derive(FromForm)]

M src/routes/mod.rs => src/routes/mod.rs +1 -0
@@ 1,3 1,4 @@
pub mod shared;
pub mod session;
pub mod document;
pub mod account;

A src/routes/shared.rs => src/routes/shared.rs +65 -0
@@ 0,0 1,65 @@
use serde_json::Value;
use rocket::request::FlashMessage;
use rocket_contrib::Template;

use ::db::PgConn;
use ::errors::Error;
use ::models::user::User;
use ::models::sheet::Sheet;

pub fn render_noauth_template(
    msg: Option<FlashMessage>,
    template: &'static str,
    content: Value,
    conn: &PgConn,
) -> Result<Template, Error> {
    render_template(None, msg, template, content, conn)
}

pub fn render_auth_template(
    user: User,
    msg: Option<FlashMessage>,
    template: &'static str,
    content: Value,
    conn: &PgConn,
) -> Result<Template, Error> {
    render_template(Some(user), msg, template, content, conn)
}

pub fn render_template(
    user: Option<User>,
    msg: Option<FlashMessage>,
    template: &'static str,
    content: Value,
    conn: &PgConn,
) -> Result<Template, Error> {
    let msg = msg.map(|msg| json!({
        "name": msg.name(),
        "msg":  msg.msg()
    }));

    let user = match user {
        Some(user) => {
            let sheets = Sheet::get_current_sheets_of(user.id, &conn)?;

            Some(json!({
                "default": user.default_character,
                "characters": sheets,
                "nickname": user.nickname
            }))
        }
        _ => {
            None
        }
    };

    Ok(Template::render(
        template,
        json!({
            "msg": msg,
            "user": user,
            "content": content,
            "hash": env!("GIT_HASH"),
        })
    ))
}

M templates/account_index.html.tera => templates/account_index.html.tera +4 -4
@@ 11,13 11,13 @@
    <a href="/document">Want to create a new one?</a>
  </p>

  {% if futures %}
  {% if content.futures %}
  <h2 class="ui header">
    To Be Validated
  </h2>

  <div class="ui middle aligned selection list">
    {% for character in futures %}
    {% for character in content.futures %}
    <div class="item">
      <img class="ui avatar image" src="/img/{{ character.content.avatar_small }}" />
      <div class="content">


@@ 30,13 30,13 @@
  </div>
  {% endif %}

  {% if currents %}
  {% if content.currents %}
  <h2 class="ui header">
    Active Characters
  </h2>

  <div class="ui middle aligned selection list">
    {% for character in currents %}
    {% for character in content.currents %}
    <div class="item">
      <img class="ui avatar image" src="/img/{{ character.content.avatar_small }}" />
      <div class="content">

M templates/base.html.tera => templates/base.html.tera +33 -18
@@ 11,29 11,44 @@
    <link rel="stylesheet" type="text/css" href="/static/ogmarkup.css" />
  </head>
  <body>
  <header class="ui text fluid">
  </header>
  <nav id="navbar" class="ui main menu text fluid sticky">
    {% if user %}
    {% include "partials/navbar-auth" %}
    {% else %}
    {% include "partials/navbar-noauth" %}
    {% endif %}
  </nav>

  <main id="main" class="ui text fluid">
    {% if msg %}
    <div class="ui positive message">
      <p>{{ msg.msg }}</p>
    <div class="ui sidebar inverted vertical menu">
      <a class="item">
        1
      </a>
      <a class="item">
        2
      </a>
      <a class="item">
        3
      </a>
    </div>
    {% endif %}
    <div class="pusher">
      <header class="ui text fluid">
      </header>
      <nav id="navbar" class="ui main menu text fluid sticky">
        {% if user %}
        {% include "partials/navbar-auth" %}
        {% else %}
        {% include "partials/navbar-noauth" %}
        {% endif %}
      </nav>

    {% block content %}{% endblock content %}
  </main>
      <main id="main" class="ui text fluid">
        {% if msg %}
        <div class="ui positive message">
          <p>{{ msg.msg }}</p>
        </div>
        {% endif %}

  <footer class="ui text fluid">
    <a href="https://git.sr.ht/~lthms/ogmios">git.sr.ht/~lthms/ogmios</a>@<a href="https://git.sr.ht/~lthms/ogmios/commit/?id={{ hash }}">{{ hash }}</a>
  </footer>
        {% block content %}{% endblock content %}
      </main>

      <footer class="ui text fluid">
        <a href="https://git.sr.ht/~lthms/ogmios">git.sr.ht/~lthms/ogmios</a>@<a href="https://git.sr.ht/~lthms/ogmios/commit/?id={{ hash }}">{{ hash }}</a>
      </footer>
    </div>
  </body>
  <script
    src="https://code.jquery.com/jquery-3.1.1.min.js"

M templates/character.html.tera => templates/character.html.tera +6 -4
@@ 2,11 2,13 @@

{% block content %}
<section class="ui text container">
  {% if sheet %}
  <img class="ui image right floated" src="/img/{{ sheet.avatar_large }}" />
  {% if content.sheet %}
  <img class="ui image right floated"
       src="/img/{{ content.sheet.avatar_large }}"
       />

  <h1 class="ui header">
    {{ sheet.name }}
    {{ content.sheet.name }}
  </h1>

  {% block characternav %}


@@ 14,7 16,7 @@

  <h2 class="ui header">History</h2>

  {{ sheet.history.rendered | safe }}
  {{ content.sheet.history.rendered | safe }}
  {% else %}
  {% block characternotfound %}
  {% endblock characternotfound %}

M templates/character_current.html.tera => templates/character_current.html.tera +3 -3
@@ 2,15 2,15 @@

{% block characternav %}
{% if user %}
{% if future %}
<a href="/character/{{ sheet.character_id }}/future">
{% if content.future %}
<a href="/character/{{ content.sheet.character_id }}/future">
  <button class="ui button">
    See next version
  </button>
</a>
{% else %}

<form method="post" action="/character/{{ sheet.character_id }}/future/new">
<form method="post" action="/character/{{ content.sheet.character_id }}/future/new">
  <button class="ui button positive submit">
    Start an update
  </button>

M templates/character_future.html.tera => templates/character_future.html.tera +4 -4
@@ 1,8 1,8 @@
{% extends "character" %}

{% block characternav %}
{% if current %}
<a href="/character/{{ sheet.character_id }}/current">
{% if content.current %}
<a href="/character/{{ content.sheet.character_id }}/current">
  <button class="ui button">
    See validated version
  </button>


@@ 10,14 10,14 @@
{% endif %}

{% if user %}
<form method="post" action="/character/{{ sheet.character_id }}/future/validate">
<form method="post" action="/character/{{ content.sheet.character_id }}/future/validate">
  <button class="ui button positive submit">
    Validate
  </button>
</form>
{% endif %}

<a href="/character/{{ sheet.character_id }}/future/edit">
<a href="/character/{{ content.sheet.character_id }}/future/edit">
  <button class="ui button icon">
    <i class="icon edit"></i>
  </button>

M templates/index.html.tera => templates/index.html.tera +1 -4
@@ 2,7 2,7 @@

{% block content %}
<p>
  Hello world, there is {{ nb }} user{{ nb|pluralize }} here.
  Hello world, there is {{ content.nb }} user{{ content.nb | pluralize }} here.
</p>
{% if user %}
<ul>


@@ 11,7 11,4 @@
  </li>
</ul>
{% endif %}

{% include "partials/lorem" %}

{% endblock content %}

M templates/partials/character_sheet.html.tera => templates/partials/character_sheet.html.tera +4 -4
@@ 1,11 1,11 @@
  <form action="{{ target }}" method="post" class="ui form" id="character_create">
  <form action="{{ content.target }}" method="post" class="ui form" id="character_create">
    <h2 class="ui dividing header">Summary</h2>

    <div class="field">
      <label>Full Name</label>
      <input type="text" name="name" placeholder="Full Name"
             {% if previous %}
             value="{{ previous.name }}"
             {% if content.previous %}
             value="{{ content.previous.name }}"
             {% endif %}/>
    </div>



@@ 49,7 49,7 @@
        name="history"
        rows="18"
        placeholder="Tell your character's story."
        >{% if previous %}{{ previous.history.raw | safe }}{% endif %}</textarea>
        >{% if content.previous %}{{ content.previous.history.raw | safe }}{% endif %}</textarea>
    </div>
    <div class="field">
      <div id="character_submit" class="ui button submit">Create</div>

M templates/partials/navbar-auth.html.tera => templates/partials/navbar-auth.html.tera +4 -1
@@ 1,5 1,8 @@
<div class="header item">
  ~{{ user.nickname }}
  <button class="ui button icon" onclick="$('.ui.sidebar').sidebar('toggle')">
    <i class="icon bars">
    </i>
  </button>
</div>

<a href="#" class="item">Explore</a>