1
1
mirror of https://github.com/aelve/guide.git synced 2024-11-23 12:15:06 +03:00

Merging for item notes

This commit is contained in:
Artyom 2016-06-19 19:20:28 +03:00
parent 392d1c592a
commit 8f4d2dd417
4 changed files with 60 additions and 19 deletions

View File

@ -405,12 +405,28 @@ makeTraitEditor =
$(sectionNode).append(area, br, cancelBtn, markdown); $(sectionNode).append(area, br, cancelBtn, markdown);
|] |]
-- | Dynamically creates a 'View.markdownEdito' (but specifically for item {- Note [blurb diffing]
~~~~~~~~~~~~~~~~~~~~~~~
A note on why we need 'empty' in 'makeItemNotesEditor'.
Assume that the notes are empty. The text in the area, therefore, will be some default blurb (# Links, #Imports, #Usage, etc). Suppose the user edits it. What will be sent to the server?
* original: blurb
* our version: modified blurb
What will happen next? The server will compare it to the value currently at the server (i.e. an empty string), and think that the blurb *was* on the server but got deleted while the client was doing editing. This is wrong, and will result in a diff popup comparing an edited blurb to an empty string. To prevent this, we pass 'empty' to 'makeItemNotesEditor' if we're using a blurb, we'll pass an empty string as the original.
-}
-- | Dynamically creates a 'View.markdownEditor' (but specifically for item
-- notes). See Note [dynamic interface]. -- notes). See Note [dynamic interface].
makeItemNotesEditor :: JSFunction a => a makeItemNotesEditor :: JSFunction a => a
makeItemNotesEditor = makeItemNotesEditor =
-- See Note [blurb diffing]
makeJSFunction "makeItemNotesEditor" makeJSFunction "makeItemNotesEditor"
["notesNode", "sectionNode", "textareaUid", "content", "itemId"] ["notesNode", "sectionNode", "textareaUid",
"empty", "content", "itemId"]
[text| [text|
$(sectionNode).html(""); $(sectionNode).html("");
area = $("<textarea>", { area = $("<textarea>", {
@ -423,7 +439,7 @@ makeItemNotesEditor =
"value" : "Save", "value" : "Save",
"type" : "button" })[0]; "type" : "button" })[0];
saveBtn.onclick = function () { saveBtn.onclick = function () {
submitItemNotes(notesNode, itemId, area.value); }; submitItemNotes(notesNode, itemId, empty ? "" : content, area.value); };
// Can't use $()-generation here because then the <span> would have // Can't use $()-generation here because then the <span> would have
// to be cloned (since we're inserting it multiple times) and I don't // to be cloned (since we're inserting it multiple times) and I don't
// know how to do that. // know how to do that.
@ -508,16 +524,28 @@ submitItemEcosystem =
submitItemNotes :: JSFunction a => a submitItemNotes :: JSFunction a => a
submitItemNotes = submitItemNotes =
makeJSFunction "submitItemNotes" ["node", "itemId", "s"] makeJSFunction "submitItemNotes"
["node", "itemId", "original", "ours"]
[text| [text|
$.post("/haskell/set/item/"+itemId+"/notes", {content: s}) $.post({
.done(function (data) { url: "/haskell/set/item/"+itemId+"/notes",
data: {
original: original,
content: ours },
success: function (data) {
$.magnificPopup.close();
$(node).replaceWith(data); $(node).replaceWith(data);
switchSection(node, "expanded"); // Switching has to be done here and not in 'Main.renderItemNotes'
// because $.post is asynchronous and will be done *after*
// switchSection has worked.
switchSection(node, "expanded"); },
statusCode: {
409: function (xhr, st, err) {
modified = xhr.responseJSON["modified"];
merged = xhr.responseJSON["merged"];
showDiffPopup(ours, modified, merged, function (x) {
submitItemNotes(node, itemId, modified, x) }); } }
}); });
// Switching has to be done here and not in 'Main.renderItemNotes'
// because $.post is asynchronous and will be done *after*
// switchSection has worked.
|] |]
-- | Add a pro to some item. -- | Add a pro to some item.
@ -554,8 +582,8 @@ submitTrait =
$.post({ $.post({
url: "/haskell/set/item/"+itemId+"/trait/"+traitId, url: "/haskell/set/item/"+itemId+"/trait/"+traitId,
data: { data: {
original : original, original: original,
content : ours }, content: ours },
success: function (data) { success: function (data) {
$.magnificPopup.close(); $.magnificPopup.close();
$(node).replaceWith(data); $(node).replaceWith(data);

View File

@ -475,12 +475,21 @@ setMethods = Spock.subcomponent "set" $ do
lucidIO $ renderItemEcosystem item lucidIO $ renderItemEcosystem item
-- Item notes -- Item notes
Spock.post (itemVar <//> "notes") $ \itemId -> do Spock.post (itemVar <//> "notes") $ \itemId -> do
original <- param' "original"
content' <- param' "content" content' <- param' "content"
invalidateCache' (CacheItemNotes itemId) modified <- view (notes.mdText) <$> dbQuery (GetItem itemId)
(edit, item) <- dbUpdate (SetItemNotes itemId content') if modified == original
addEdit edit then do
category <- dbQuery (GetCategoryByItem itemId) invalidateCache' (CacheItemNotes itemId)
lucidIO $ renderItemNotes category item (edit, item) <- dbUpdate (SetItemNotes itemId content')
addEdit edit
category <- dbQuery (GetCategoryByItem itemId)
lucidIO $ renderItemNotes category item
else do
setStatus HTTP.status409
json $ M.fromList [
("modified" :: Text, modified),
("merged" :: Text, merge original content' modified)]
-- Trait -- Trait
Spock.post (itemVar <//> traitVar) $ \itemId traitId -> do Spock.post (itemVar <//> traitVar) $ \itemId traitId -> do
original <- param' "original" original <- param' "original"

View File

@ -810,6 +810,8 @@ renderItemInfo cat item = cached (CacheItemInfo (item^.uid)) $ do
let bg = hueToDarkColor $ getItemHue cat item let bg = hueToDarkColor $ getItemHue cat item
let thisId = "item-info-" <> uidToText (item^.uid) let thisId = "item-info-" <> uidToText (item^.uid)
this = JS.selectId thisId this = JS.selectId thisId
let bodyNode = JS.selectChildren (JS.selectParent this)
(JS.selectClass "item-body")
div_ [id_ thisId, class_ "item-info", div_ [id_ thisId, class_ "item-info",
style_ ("background-color:" <> bg)] $ do style_ ("background-color:" <> bg)] $ do
@ -836,8 +838,6 @@ renderItemInfo cat item = cached (CacheItemInfo (item^.uid)) $ do
section "editing" [] $ do section "editing" [] $ do
-- When the info/header node changes its group (and is hence -- When the info/header node changes its group (and is hence
-- recolored), item's body has to be recolored too -- recolored), item's body has to be recolored too
let bodyNode = JS.selectChildren (JS.selectParent this)
(JS.selectClass "item-body")
let formSubmitHandler formNode = let formSubmitHandler formNode =
JS.submitItemInfo (this, bodyNode, item^.uid, formNode) JS.submitItemInfo (this, bodyNode, item^.uid, formNode)
form_ [onFormSubmit formSubmitHandler] $ do form_ [onFormSubmit formSubmitHandler] $ do
@ -1124,6 +1124,8 @@ renderItemNotes category item = cached (CacheItemNotes (item^.uid)) $ do
JS.makeItemNotesEditor ( JS.makeItemNotesEditor (
this, JS.selectUid editingSectionUid, this, JS.selectUid editingSectionUid,
textareaUid, textareaUid,
-- See Note [blurb diffing]
markdownNull (item^.notes),
contents, contents,
item^.uid) <> item^.uid) <>
JS.switchSection (this, "editing" :: Text) <> JS.switchSection (this, "editing" :: Text) <>

View File

@ -216,6 +216,8 @@ textarea.fullwidth {
} }
.diff-choices > * > .text { .diff-choices > * > .text {
white-space: pre-wrap;
font-family: monospace;
border: 1px solid gray; border: 1px solid gray;
padding: 5px 10px; padding: 5px 10px;
margin: 5px 0px; margin: 5px 0px;