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

Add item notes

This commit is contained in:
Artyom 2016-03-05 00:40:51 +03:00
parent c5cdcbaebf
commit 6126d1e426
3 changed files with 105 additions and 16 deletions

View File

@ -41,14 +41,15 @@ allJSFunctions = JS . T.unlines . map fromJS $ [
-- Add methods
addLibrary, addCategory,
addPro, addCon,
-- Render-as-editable methods
-- “Render this in a different way” methods
setCategoryTitleMode, setCategoryNotesMode,
setItemInfoMode, setItemTraitsMode,
setItemInfoMode, setItemTraitsMode, setItemNotesMode,
setTraitMode,
-- Set methods
submitCategoryTitle, submitCategoryNotes,
-- TODO: rename this to submitItemHeader or something?
submitItemInfo, submitItemNotes,
submitTrait,
submitItemInfo,
-- Other things
moveTraitUp, moveTraitDown, deleteTrait,
moveItemUp, moveItemDown, deleteItem ]
@ -254,6 +255,22 @@ submitCategoryNotes =
.done(replaceWithData(node));
|]
setItemNotesMode :: JSFunction a => a
setItemNotesMode =
makeJSFunction "setItemNotesMode" ["node", "itemId", "mode"]
[text|
$.get("/render/item/"+itemId+"/notes", {mode: mode})
.done(replaceWithData(node));
|]
submitItemNotes :: JSFunction a => a
submitItemNotes =
makeJSFunction "submitItemNotes" ["node", "itemId", "s"]
[text|
$.post("/set/item/"+itemId+"/notes", {content: s})
.done(replaceWithData(node));
|]
-- | Add a pro to some item.
addPro :: JSFunction a => a
addPro =
@ -306,22 +323,23 @@ submitTrait =
submitItemInfo :: JSFunction a => a
submitItemInfo =
makeJSFunction "submitItemInfo" ["infoNode", "traitsNode", "itemId", "form"]
makeJSFunction "submitItemInfo" ["infoNode", "otherNodes", "itemId", "form"]
[text|
// If the group was changed, we need to recolor the whole item,
// but we don't want to rerender the item on the server because
// it would lose the item's state (e.g. what if the traits were
// being edited? etc). So, instead we query colors from the server
// and change the color of the traits div manually.
// and change the color of the other divs (traits, notes, etc)
// manually.
$.post("/set/item/"+itemId+"/info", $(form).serialize())
.done(function (data) {
// Note the order first we change the color, then we replace
// the info node. The reason is that otherwise the traitsNode
// the info node. The reason is that otherwise the otherNodes
// selector might become invalid (if it depends on the infoNode
// selector).
$.get("/render/item/"+itemId+"/colors")
.done(function (colors) {
$(traitsNode).css("background-color", colors.light);
$(otherNodes).css("background-color", colors.light);
replaceWithData(infoNode)(data);
});
});

View File

@ -83,9 +83,6 @@ hackageLibrary = Library True
deriveSafeCopy 0 'base ''ItemKind
makeFields ''ItemKind
-- TODO: add usage notes! and then change the rules to say “add it to item
-- notes”, not “add it to category notes”
-- TODO: add a field like “people to ask on IRC about this library if you
-- need help”
data Item = Item {
@ -94,9 +91,12 @@ data Item = Item {
_itemGroup_ :: Maybe Text,
_itemPros :: [Trait],
_itemCons :: [Trait],
_itemNotes :: Text,
_itemLink :: Maybe Url,
_itemKind :: ItemKind }
-- TODO: make a 'Markdown' type alias?
deriveSafeCopy 0 'base ''Item
makeFields ''Item
@ -263,6 +263,7 @@ addItem catId itemId name' kind' = do
_itemGroup_ = Nothing,
_itemPros = [],
_itemCons = [],
_itemNotes = "",
_itemLink = Nothing,
_itemKind = kind' }
categoryById catId . items %= (++ [newItem])
@ -351,6 +352,11 @@ setItemOnHackage itemId onHackage' = do
itemById itemId . kind . onHackage .= onHackage'
use (itemById itemId)
setItemNotes :: Uid -> Text -> Acid.Update GlobalState Item
setItemNotes itemId notes' = do
itemById itemId . notes .= notes'
use (itemById itemId)
setTraitContent :: Uid -> Uid -> Text -> Acid.Update GlobalState Trait
setTraitContent itemId traitId content' = do
itemById itemId . traitById traitId . content .= content'
@ -436,6 +442,7 @@ makeAcidic ''GlobalState [
-- set
'setCategoryTitle, 'setCategoryNotes,
'setItemName, 'setItemLink, 'setItemGroup, 'setItemKind, 'setItemOnHackage,
'setItemNotes,
'setTraitContent,
-- delete
'deleteItem,
@ -492,6 +499,17 @@ sampleState = do
to do with lens than with other libraries), your code may start
not looking like Haskell much
(see [this post](https://ro-che.info/articles/2014-04-24-lens-unidiomatic)).|] ],
_itemNotes = [text|
Get a value:
> (1,2) ^. _1
1
Set a value:
> (1,2) & _1 .~ 10
(10, 2)
|],
_itemLink = Nothing,
_itemKind = hackageLibrary }
let microlensItem = Item {
@ -511,6 +529,7 @@ sampleState = do
Trait "134" $ T.unwords $ T.lines [text|
Doesn't let you write code in fully lensy style (since it
omits lots of operators and `*Of` functions from lens).|] ],
_itemNotes = "",
_itemLink = Just "https://github.com/aelve/microlens",
_itemKind = hackageLibrary }
let lensesCategory = Category {
@ -527,6 +546,7 @@ sampleState = do
_itemPros = [Trait "211" "the most widely used package",
Trait "213" "has lots of tutorials, book coverage, etc"],
_itemCons = [Trait "212" "development has stagnated"],
_itemNotes = "",
_itemLink = Nothing,
_itemKind = hackageLibrary }
let megaparsecItem = Item {
@ -537,6 +557,7 @@ sampleState = do
\so existing tutorials/code samples \
\could be reused and migration is easy"],
_itemCons = [],
_itemNotes = "",
_itemLink = Nothing,
_itemKind = hackageLibrary }
let attoparsecItem = Item {
@ -546,6 +567,7 @@ sampleState = do
_itemPros = [Trait "231" "very fast, good for parsing binary formats"],
_itemCons = [Trait "232" "can't report positions of parsing errors",
Trait "234" "doesn't provide a monad transformer"],
_itemNotes = "",
_itemLink = Nothing,
_itemKind = hackageLibrary }
let parsingCategory = Category {
@ -562,6 +584,7 @@ sampleState = do
_itemGroup_ = Nothing,
_itemPros = [],
_itemCons = [],
_itemNotes = "",
_itemLink = Nothing,
_itemKind = hackageLibrary }
let item1 = def {
@ -640,6 +663,12 @@ renderMethods = Spock.subcomponent "render" $ do
renderMode <- param' "mode"
category <- dbQuery (GetCategoryByItem itemId)
lucid $ renderItemInfo renderMode category item
-- Item notes
Spock.get (itemVar <//> "notes") $ \itemId -> do
item <- dbQuery (GetItem itemId)
renderMode <- param' "mode"
category <- dbQuery (GetCategoryByItem itemId)
lucid $ renderItemNotes renderMode category item
-- All item traits
Spock.get (itemVar <//> "traits") $ \itemId -> do
item <- dbQuery (GetItem itemId)
@ -693,6 +722,12 @@ setMethods = Spock.subcomponent "set" $ do
item <- dbQuery (GetItem itemId)
category <- dbQuery (GetCategoryByItem itemId)
lucid $ renderItemInfo Editable category item
-- Item notes
Spock.post (itemVar <//> "notes") $ \itemId -> do
content' <- param' "content"
item <- dbUpdate (SetItemNotes itemId content')
category <- dbQuery (GetCategoryByItem itemId)
lucid $ renderItemNotes Editable category item
-- Trait
Spock.post (itemVar <//> traitVar) $ \itemId traitId -> do
content' <- param' "content"
@ -1011,6 +1046,7 @@ renderItem editable cat item =
renderItemTraits Normal cat item
Editable -> do
renderItemTraits Editable cat item
renderItemNotes Editable cat item
-- TODO: find some way to give all functions access to category and item (or
-- category, item and trait) without passing everything explicitly?
@ -1045,10 +1081,10 @@ renderItemInfo editable cat item = do
-- TODO: link to Stackage too
-- TODO: should check for Stackage automatically
InEdit -> do
let traitsNode = selectParent infoNode `selectChild`
selectClass "item-traits"
let otherNodes = selectChild (selectParent infoNode)
(selectClass "item-body")
let formSubmitHandler formNode =
JS.submitItemInfo (infoNode, traitsNode, item^.uid, formNode)
JS.submitItemInfo (infoNode, otherNodes, item^.uid, formNode)
form_ [onFormSubmit formSubmitHandler] $ do
label_ $ do
"Package name"
@ -1109,8 +1145,10 @@ renderItemTraits :: Editable -> Category -> Item -> HtmlT IO ()
renderItemTraits editable cat item = do
let bg = hueToLightColor $ getItemHue cat item
-- If the structure of HTML changes here, don't forget to update the
-- 'traitsNode' selector in 'renderItemInfo'.
div_ [class_ "item-traits", style_ ("background-color:" <> bg)] $ do
-- 'otherNodes' selector in 'renderItemInfo'. Specifically, we depend on
-- having a div with a class “item-body”.
div_ [class_ "item-traits item-body",
style_ ("background-color:" <> bg)] $ do
this <- thisNode
div_ [class_ "traits-groups-container"] $ do
div_ [class_ "traits-group"] $ do
@ -1179,6 +1217,39 @@ renderTrait InEdit itemId trait = li_ $ do
textButton "cancel" $
JS.setTraitMode (this, itemId, trait^.uid, Editable)
renderItemNotes :: Editable -> Category -> Item -> HtmlT IO ()
renderItemNotes editable category item = do
let bg = hueToLightColor $ getItemHue category item
-- If the structure of HTML changes here, don't forget to update the
-- 'otherNodes' selector in 'renderItemInfo'. Specifically, we depend on
-- having a div with a class “item-body”.
div_ [class_ "item-notes item-body",
style_ ("background-color:" <> bg)] $ do
-- TODO: this duplicates code from renderCategoryNotes, try to reduce
-- duplication
this <- thisNode
case editable of
Editable -> do
-- TODO: use shortcut-links
renderMarkdownBlock (item^.notes)
-- TODO: “show notes and examples”
textButton "edit notes" $
JS.setItemNotesMode (this, item^.uid, InEdit)
InEdit -> do
textareaId <- randomUid
textarea_ [uid_ textareaId, rows_ "10", class_ "fullwidth"] $
toHtml (item^.notes)
button "Save" [] $ do
-- «$("#<textareaId>").val()» is a Javascript expression that
-- returns text contained in the textarea
let textareaValue = JS $ format "$(\"#{}\").val()" [textareaId]
JS.submitItemNotes (this, item^.uid, textareaValue)
emptySpan "6px"
button "Cancel" [] $
JS.setItemNotesMode (this, item^.uid, Editable)
emptySpan "6px"
"Markdown"
-- Utils
onPageLoad :: JS -> HtmlT IO ()

View File

@ -27,7 +27,7 @@ body {
.item-info {
padding: 10px 15px; }
.item-traits {
.item-traits, .item-notes {
padding: 10px 15px 20px 15px; }
.traits-groups-container {