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:
parent
392d1c592a
commit
8f4d2dd417
52
src/JS.hs
52
src/JS.hs
@ -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);
|
||||||
|
19
src/Main.hs
19
src/Main.hs
@ -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"
|
||||||
|
@ -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) <>
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user