feat: add new gist form gets error handling and rendering with

user-provided values on error
master
Aravinth Manivannan 2022-02-27 20:14:53 +05:30
parent 7fdf815a14
commit 92f48b7bfb
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
3 changed files with 127 additions and 50 deletions

View File

@ -55,11 +55,37 @@ impl CtxError for NewGist {
}
impl NewGist {
pub fn new(username: &str, settings: &Settings, payload: Option<&[FieldNames<&str>]>) -> Self {
let ctx = RefCell::new(auth_ctx(username, settings));
if let Some(payload) = payload {
ctx.borrow_mut().insert(PAYLOAD_KEY, &payload);
pub fn new(
username: &str,
settings: &Settings,
description: Option<&str>,
payload: Option<&[FieldNames<&str>]>,
) -> Self {
const FIELDNAMES_KEY: &str = "fieldnames";
let mut ctx = auth_ctx(username, settings);
ctx.insert("visibility_private", GistVisibility::Private.to_str());
ctx.insert("visibility_unlisted", GistVisibility::Unlisted.to_str());
ctx.insert("visibility_public", GistVisibility::Public.to_str());
if let Some(description) = description {
ctx.insert("description", description);
}
if let Some(payload) = payload {
ctx.insert(PAYLOAD_KEY, &payload);
let fields = payload.len();
let mut fieldnames = Vec::with_capacity(fields);
for i in 1..=payload.len() {
fieldnames.push(FieldNames::<String>::new(i))
}
ctx.insert(FIELDNAMES_KEY, &fieldnames);
} else {
ctx.insert(FIELDNAMES_KEY, &[FieldNames::<String>::new(1)]);
ctx.insert(PAYLOAD_KEY, &[FieldNames::<&'static str>::default()]);
}
println!("{:?}", ctx.get(PAYLOAD_KEY));
let ctx = RefCell::new(ctx);
Self { ctx }
}
@ -68,7 +94,7 @@ impl NewGist {
}
pub fn page(username: &str, s: &Settings) -> String {
let p = Self::new(username, s, None);
let p = Self::new(username, s, None, None);
p.render()
}
}
@ -90,6 +116,15 @@ pub struct FieldNames<T: Serialize + ToString> {
pub content: T,
}
impl Default for FieldNames<&'static str> {
fn default() -> Self {
Self {
content: "",
filename: "",
}
}
}
impl<T: Serialize + ToString> From<FieldNames<T>> for FileInfo {
fn from(f: FieldNames<T>) -> Self {
FileInfo {
@ -164,6 +199,7 @@ fn get_description(payload: &serde_json::Value) -> Option<&str> {
}
None
}
#[cfg(test)]
mod tests {
use serde_json::json;
@ -267,9 +303,9 @@ mod tests {
);
let some_partially_empty_files = json!({
f1.filename.clone(): f1_name,
f1.content.clone(): f1_content,
f2.content.clone(): f2_content,
f1.filename: f1_name,
f1.content: f1_content,
f2.content: f2_content,
});
let some_empty_gist_err = FieldNames::<&str>::from_serde_json(&some_partially_empty_files);
assert!(some_empty_gist_err.is_err());

View File

@ -224,5 +224,28 @@ footer {
width: 100%;
margin: 10px 0;
padding: 5px 0;
height: 350px;
height: 320px;
}
.gist__button-group {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
}
.gist__button-container {
flex: 1;
max-width: 200px;
}
.form__submit--secondary {
width: 100%;
display: block;
margin: 10px 0;
border: none;
padding: 5px 0;
cursor: pointer;
background-color: #e9e9ed;
}

View File

@ -1,8 +1,8 @@
{% extends 'gistbase' %}
{% block gist_main %}
<form class="gist__new" action="/new" method="POST" accept-charset="utf-8">
<form class="gist__new" action={{ page.gist.new }} method="POST" accept-charset="utf-8">
{% include "error_comp" %}
<label class="form__label" for="login">
<label class="form__label" for="description">
Gist description
<input
class="form__input"
@ -15,61 +15,79 @@
{% endif %}
/>
</label>
{% if payload.files %}
{% for file in payload.files %}
{% for fieldname in fieldnames%}
{% set content = payload | nth(n=(loop.index - 1)) %}
<legend class="gist__file">
<label class="form__label" for="login">
<label class="form__label" for={{ fieldname.filename }}>
File name with extension
<input
required
class="form__input"
name="filename"
name={{ fieldname.filename }}
autofocus
id="filename"
id={{ fieldname.filename }}
type="text"
value={{ file.filename }}
value="{{ content.filename }}"
/>
</label>
<label class="form__label" for="login">
<label class="form__label" for={{ fieldname.content }}>
<textarea
required
class="gist__file-content"
name="content"
autofocus
id="content"
name={{ fieldname.content }}
id={{ fieldname.content }}
type="text"
value={{ file.content }}
>
</textarea>
>{{ content.content }}</textarea>
</label>
</legend>
{% endfor %}
{% else %}
<legend class="gist__file">
<label class="form__label" for="login">
File name with extension
<input
class="form__input"
name="filename"
autofocus
id="filename"
type="text"
/>
</label>
<label class="form__label" for="login">
<textarea
required
class="gist__file-content"
name="content"
autofocus
id="content"
type="text"
>
</textarea>
</label>
</legend>
<div class="gist__radio-group">
<label class="gist__radio-label" for={{ visibility_public }}>
<input
required
class="gist__radio-btn"
name="visibility"
id={{ visibility_public }}
type="radio"
value={{ visibility_public }}
/>
Public
</label>
{% endif %}
<label class="gist__radio-label" for={{ visibility_unlisted }}>
<input
class="gist__radio-btn"
name="visibility"
id={{ visibility_unlisted }}
type="radio"
value={{ visibility_unlisted }}
/>
Unlisted
</label>
<label class="gist__radio-label" for={{ visibility_private }}>
<input
class="gist__radio-btn"
name="visibility"
id={{ visibility_private }}
type="radio"
value={{ visibility_private }}
/>
Private
</label>
</div>
<div class="gist__button-group">
<div class="gist__button-container">
<button
class="form__submit--secondary"
name="add_file"
value="true"
type="submit"
>Add File</button>
</div>
<div class="gist__button-container">
<button class="form__submit" type="submit">Create Gist</button>
</div>
</div>
</form>
{% endblock %}