04626ceb0c598709c67700277346313e42810666 — Thomas Letan 1 year, 3 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>