Expose merge conflicts in the JavaScript editor. This fixes #23

This commit is contained in:
Magnus Hoff 2017-11-20 16:37:52 +01:00
parent b685139d5b
commit a09aa4b601
3 changed files with 71 additions and 30 deletions

View file

@ -78,15 +78,27 @@ function openEditor() {
// Update body:
rendered.innerHTML = result.rendered;
if (result.conflict) {
form.elements.title.value = result.title;
form.elements.body.value = result.body;
}
// Update form:
form.elements.base_revision.value = result.revision;
for (const element of form.elements) {
element.defaultValue = element.value;
}
container.classList.remove('edit');
if (!result.conflict) {
container.classList.remove('edit');
}
textarea.disabled = false;
if (result.conflict) {
alert("Your edit came into conflict with another change and has not been saved.\n" +
"Please resolve the merge conflict and save again.");
}
}).catch(err => {
textarea.disabled = false;
console.error(err);

View file

@ -133,9 +133,11 @@ impl Resource for ArticleResource {
#[derive(Serialize)]
struct PutResponse<'a> {
conflict: bool,
slug: &'a str,
revision: i32,
title: &'a str,
body: Option<&'a str>,
rendered: &'a str,
last_updated: &'a str,
}
@ -150,26 +152,54 @@ impl Resource for ArticleResource {
.and_then(move |update: UpdateArticle| {
self.state.update_article(self.article_id, update.base_revision, update.title, update.body, identity)
})
.and_then(|updated| {
let updated = updated.unwrap();
futures::finished(Response::new()
.with_status(hyper::StatusCode::Ok)
.with_header(ContentType(APPLICATION_JSON.clone()))
.with_body(serde_json::to_string(&PutResponse {
slug: &updated.slug,
revision: updated.revision,
title: &updated.title,
rendered: &Template {
.and_then(|updated| match updated {
UpdateResult::Success(updated) =>
Ok(Response::new()
.with_status(hyper::StatusCode::Ok)
.with_header(ContentType(APPLICATION_JSON.clone()))
.with_body(serde_json::to_string(&PutResponse {
conflict: false,
slug: &updated.slug,
revision: updated.revision,
title: &updated.title,
rendered: render_markdown(&updated.body),
}.to_string(),
last_updated: &last_updated(
updated.article_id,
&Local.from_utc_datetime(&updated.created),
updated.author.as_ref().map(|x| &**x)
),
}).expect("Should never fail"))
)
body: None,
rendered: &Template {
title: &updated.title,
rendered: render_markdown(&updated.body),
}.to_string(),
last_updated: &last_updated(
updated.article_id,
&Local.from_utc_datetime(&updated.created),
updated.author.as_ref().map(|x| &**x)
),
}).expect("Should never fail"))
),
UpdateResult::RebaseConflict(RebaseConflict {
base_article, title, body
}) => {
let title = title.flatten();
let body = body.flatten();
Ok(Response::new()
.with_status(hyper::StatusCode::Ok)
.with_header(ContentType(APPLICATION_JSON.clone()))
.with_body(serde_json::to_string(&PutResponse {
conflict: true,
slug: &base_article.slug,
revision: base_article.revision,
title: &title,
body: Some(&body),
rendered: &Template {
title: &title,
rendered: render_markdown(&body),
}.to_string(),
last_updated: &last_updated(
base_article.article_id,
&Local.from_utc_datetime(&base_article.created),
base_article.author.as_ref().map(|x| &**x)
),
}).expect("Should never fail"))
)
}
})
)
}

View file

@ -58,16 +58,6 @@ pub enum UpdateResult {
RebaseConflict(RebaseConflict),
}
impl UpdateResult {
// TODO Move to mod tests below
pub fn unwrap(self) -> models::ArticleRevision {
match self {
UpdateResult::Success(x) => x,
_ => panic!("Expected success")
}
}
}
fn decide_slug(conn: &SqliteConnection, article_id: i32, prev_title: &str, title: &str, prev_slug: Option<&str>) -> Result<String, Error> {
let base_slug = ::slug::slugify(title);
@ -485,6 +475,15 @@ mod test {
use super::*;
use db;
impl UpdateResult {
pub fn unwrap(self) -> models::ArticleRevision {
match self {
UpdateResult::Success(x) => x,
_ => panic!("Expected success")
}
}
}
macro_rules! init {
($state:ident) => {
let db = db::test_connection();