display API/Validation errors in UI

This commit is contained in:
Jon Schoning 2021-10-02 22:31:51 -05:00 committed by Yann Esposito (Yogsototh)
parent 9682a0c9c1
commit 5f178e59bd
Signed by untrusted user who does not match committer: yogsototh
GPG key ID: 7B19A4C650D59646
12 changed files with 124 additions and 60 deletions

View file

@ -48,9 +48,9 @@ editBookmark :: Bookmark -> Aff (Either Error (Response String))
editBookmark bm = do editBookmark bm = do
fetchJson POST "api/add" (Just (Bookmark' bm)) AXRes.string fetchJson POST "api/add" (Just (Bookmark' bm)) AXRes.string
editNote :: Note -> Aff (Either Error (Response Json)) editNote :: Note -> Aff (Either Error (Response String))
editNote bm = do editNote bm = do
fetchJson POST "api/note/add" (Just (Note' bm)) AXRes.json fetchJson POST "api/note/add" (Just (Note' bm)) AXRes.string
lookupTitle :: Bookmark -> Aff (Maybe String) lookupTitle :: Bookmark -> Aff (Maybe String)
lookupTitle bm = do lookupTitle bm = do

View file

@ -10,7 +10,7 @@ import Halogen as H
import Halogen.HTML (div, input, text) import Halogen.HTML (div, input, text)
import Halogen.HTML.Elements (label) import Halogen.HTML.Elements (label)
import Halogen.HTML.Events (onChecked) import Halogen.HTML.Events (onChecked)
import Halogen.HTML.Properties (InputType(..), checked, for, id_, name, type_) import Halogen.HTML.Properties (InputType(..), checked, for, id, name, type_)
import Model (AccountSettings) import Model (AccountSettings)
import Util (class_) import Util (class_)
import Web.Event.Event (Event) import Web.Event.Event (Event)
@ -52,19 +52,19 @@ usetting u' =
div [ class_ "settings-form" ] div [ class_ "settings-form" ]
[ div [ class_ "fw7 mb2"] [ text "Account Settings" ] [ div [ class_ "fw7 mb2"] [ text "Account Settings" ]
, div [ class_ "flex items-center mb2" ] , div [ class_ "flex items-center mb2" ]
[ input [ type_ InputCheckbox , class_ "pointer mr2" , id_ "archiveDefault", name "archiveDefault" [ input [ type_ InputCheckbox , class_ "pointer mr2" , id "archiveDefault", name "archiveDefault"
, checked (us.archiveDefault) , onChecked (editField EarchiveDefault) ] , checked (us.archiveDefault) , onChecked (editField EarchiveDefault) ]
, label [ for "archiveDefault", class_ "lh-copy" ] , label [ for "archiveDefault", class_ "lh-copy" ]
[ text "Archive Non-Private Bookmarks (archive.li)" ] [ text "Archive Non-Private Bookmarks (archive.li)" ]
] ]
, div [ class_ "flex items-center mb2" ] , div [ class_ "flex items-center mb2" ]
[ input [ type_ InputCheckbox , class_ "pointer mr2" , id_ "privateDefault", name "privateDefault" [ input [ type_ InputCheckbox , class_ "pointer mr2" , id "privateDefault", name "privateDefault"
, checked (us.privateDefault) , onChecked (editField EprivateDefault) ] , checked (us.privateDefault) , onChecked (editField EprivateDefault) ]
, label [ for "privateDefault", class_ "lh-copy" ] , label [ for "privateDefault", class_ "lh-copy" ]
[ text "Default new bookmarks to Private" ] [ text "Default new bookmarks to Private" ]
] ]
, div [ class_ "flex items-center mb2" ] , div [ class_ "flex items-center mb2" ]
[ input [ type_ InputCheckbox , class_ "pointer mr2" , id_ "privacyLock", name "privacyLock" [ input [ type_ InputCheckbox , class_ "pointer mr2" , id "privacyLock", name "privacyLock"
, checked (us.privacyLock) , onChecked (editField EprivacyLock) ] , checked (us.privacyLock) , onChecked (editField EprivacyLock) ]
, label [ for "privacyLock", class_ "lh-copy" ] , label [ for "privacyLock", class_ "lh-copy" ]
[ text "Privacy Lock (Private Account)" ] [ text "Privacy Lock (Private Account)" ]

View file

@ -7,18 +7,18 @@ import Affjax.StatusCode (StatusCode(..))
import App (destroy, editBookmark, lookupTitle) import App (destroy, editBookmark, lookupTitle)
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Lens (Lens', lens, use, (%=), (.=)) import Data.Lens (Lens', lens, use, (%=), (.=))
import Data.Maybe (Maybe(..), maybe, isJust) import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe)
import Data.Monoid (guard) import Data.Monoid (guard)
import Data.String (Pattern(..), null, stripPrefix) import Data.String (Pattern(..), null, stripPrefix)
import Data.Tuple (fst, snd) import Data.Tuple (fst, snd)
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Console (log) import Effect.Console (log)
import Globals (app', closeWindow, mmoment8601) import Globals (closeWindow, mmoment8601)
import Halogen as H import Halogen as H
import Halogen.HTML (button, div, form, input, label, p, span, table, tbody_, td, td_, text, textarea, tr_) import Halogen.HTML (button, div, form, input, label, p, span, table, tbody_, td, td_, text, textarea, tr_)
import Halogen.HTML.Events (onSubmit, onValueChange, onChecked, onClick) import Halogen.HTML.Events (onSubmit, onValueChange, onChecked, onClick)
import Halogen.HTML.Properties (ButtonType(..), InputType(..), autocomplete, autofocus, checked, disabled, for, id_, name, required, rows, title, type_, value) import Halogen.HTML.Properties (ButtonType(..), InputType(..), autocomplete, autofocus, checked, disabled, for, id, name, required, rows, title, type_, value)
import Model (Bookmark) import Model (Bookmark)
import Util (_curQuerystring, _loc, _doc, _lookupQueryStringValue, attr, class_, ifElseH, whenH) import Util (_curQuerystring, _loc, _doc, _lookupQueryStringValue, attr, class_, ifElseH, whenH)
import Web.Event.Event (Event, preventDefault) import Web.Event.Event (Event, preventDefault)
@ -47,6 +47,7 @@ type BState =
, deleteAsk :: Boolean , deleteAsk :: Boolean
, loading :: Boolean , loading :: Boolean
, destroyed :: Boolean , destroyed :: Boolean
, apiError :: Maybe String
} }
_bm :: Lens' BState Bookmark _bm :: Lens' BState Bookmark
@ -55,6 +56,9 @@ _bm = lens _.bm (_ { bm = _ })
_edit_bm :: Lens' BState Bookmark _edit_bm :: Lens' BState Bookmark
_edit_bm = lens _.edit_bm (_ { edit_bm = _ }) _edit_bm = lens _.edit_bm (_ { edit_bm = _ })
_apiError :: Lens' BState (Maybe String)
_apiError = lens _.apiError (_ { apiError = _ })
addbmark :: forall q i o. Bookmark -> H.Component q i o Aff addbmark :: forall q i o. Bookmark -> H.Component q i o Aff
addbmark b' = addbmark b' =
H.mkComponent H.mkComponent
@ -63,7 +67,6 @@ addbmark b' =
, eval: H.mkEval $ H.defaultEval { handleAction = handleAction } , eval: H.mkEval $ H.defaultEval { handleAction = handleAction }
} }
where where
app = app' unit
mkState b = mkState b =
{ bm: b { bm: b
@ -71,10 +74,11 @@ addbmark b' =
, deleteAsk: false , deleteAsk: false
, destroyed: false , destroyed: false
, loading: false , loading: false
, apiError: Nothing
} }
render :: forall m. BState -> H.ComponentHTML BAction () m render :: forall m. BState -> H.ComponentHTML BAction () m
render s@{ bm, edit_bm } = render s@{ bm, edit_bm, apiError } =
ifElseH (not s.destroyed) ifElseH (not s.destroyed)
display_edit display_edit
display_destroyed display_destroyed
@ -86,39 +90,41 @@ addbmark b' =
[ tr_ [ tr_
[ td [ class_ "w1" ] [ ] [ td [ class_ "w1" ] [ ]
, td_ [ whenH (bm.bid > 0) , td_ [ whenH (bm.bid > 0)
display_exists display_exists,
whenH (isJust apiError)
(alert_notification (fromMaybe "" apiError))
] ]
] ]
, tr_ , tr_
[ td_ [ label [ for "url" ] [ text "URL" ] ] [ td_ [ label [ for "url" ] [ text "URL" ] ]
, td_ [ input [ type_ InputUrl , id_ "url", class_ "w-100 mv1" , required true, name "url", autofocus (null bm.url) , td_ [ input [ type_ InputUrl , id "url", class_ "w-100 mv1" , required true, name "url", autofocus (null bm.url)
, value (edit_bm.url) , onValueChange (editField Eurl)] ] , value (edit_bm.url) , onValueChange (editField Eurl)] ]
] ]
, tr_ , tr_
[ td_ [ label [ for "title" ] [ text "title" ] ] [ td_ [ label [ for "title" ] [ text "title" ] ]
, td [class_ "flex"] , td [class_ "flex"]
[ input [ type_ InputText , id_ "title", class_ "w-100 mv1 flex-auto" , name "title" , value (edit_bm.title) , onValueChange (editField Etitle)] [ input [ type_ InputText , id "title", class_ "w-100 mv1 flex-auto" , name "title" , value (edit_bm.title) , onValueChange (editField Etitle)]
, button [ disabled s.loading, type_ ButtonButton, onClick \_ -> BLookupTitle, class_ ("ml2 input-reset ba b--navy pointer f6 di dim pa1 ma1 mr0 " <> guard s.loading "bg-light-silver") ] [ text "fetch" ] , button [ disabled s.loading, type_ ButtonButton, onClick \_ -> BLookupTitle, class_ ("ml2 input-reset ba b--navy pointer f6 di dim pa1 ma1 mr0 " <> guard s.loading "bg-light-silver") ] [ text "fetch" ]
] ]
] ]
, tr_ , tr_
[ td_ [ label [ for "description" ] [ text "description" ] ] [ td_ [ label [ for "description" ] [ text "description" ] ]
, td_ [ textarea [ class_ "w-100 mt1 mid-gray" , id_ "description", name "description", rows 4 , td_ [ textarea [ class_ "w-100 mt1 mid-gray" , id "description", name "description", rows 4
, value (edit_bm.description) , onValueChange (editField Edescription)] ] , value (edit_bm.description) , onValueChange (editField Edescription)] ]
] ]
, tr_ , tr_
[ td_ [ label [ for "tags" ] [ text "tags" ] ] [ td_ [ label [ for "tags" ] [ text "tags" ] ]
, td_ [ input [ type_ InputText , id_ "tags", class_ "w-100 mv1" , name "tags", autocomplete false, attr "autocapitalize" "off", autofocus (not $ null bm.url) , td_ [ input [ type_ InputText , id "tags", class_ "w-100 mv1" , name "tags", autocomplete false, attr "autocapitalize" "off", autofocus (not $ null bm.url)
, value (edit_bm.tags) , onValueChange (editField Etags)] ] , value (edit_bm.tags) , onValueChange (editField Etags)] ]
] ]
, tr_ , tr_
[ td_ [ label [ for "private" ] [ text "private" ] ] [ td_ [ label [ for "private" ] [ text "private" ] ]
, td_ [ input [ type_ InputCheckbox , id_ "private", class_ "private pointer" , name "private" , td_ [ input [ type_ InputCheckbox , id "private", class_ "private pointer" , name "private"
, checked (edit_bm.private) , onChecked (editField Eprivate)] ] , checked (edit_bm.private) , onChecked (editField Eprivate)] ]
] ]
, tr_ , tr_
[ td_ [ label [ for "toread" ] [ text "read later" ] ] [ td_ [ label [ for "toread" ] [ text "read later" ] ]
, td_ [ input [ type_ InputCheckbox , id_ "toread", class_ "toread pointer" , name "toread" , td_ [ input [ type_ InputCheckbox , id "toread", class_ "toread pointer" , name "toread"
, checked (edit_bm.toread) , onChecked (editField Etoread)] ] , checked (edit_bm.toread) , onChecked (editField Etoread)] ]
] ]
, tr_ , tr_
@ -146,6 +152,9 @@ addbmark b' =
] ]
] ]
alert_notification alert_text _ =
div [ class_ "alert alert-err" ] [ text alert_text ]
display_destroyed _ = p [ class_ "red"] [text "you killed this bookmark"] display_destroyed _ = p [ class_ "red"] [text "you killed this bookmark"]
editField :: forall a. (a -> EditField) -> a -> BAction editField :: forall a. (a -> EditField) -> a -> BAction
@ -186,8 +195,10 @@ addbmark b' =
handleAction (BEditSubmit e) = do handleAction (BEditSubmit e) = do
liftEffect (preventDefault e) liftEffect (preventDefault e)
edit_bm <- use _edit_bm edit_bm <- use _edit_bm
_apiError .= Nothing
H.liftAff (editBookmark edit_bm) >>= case _ of H.liftAff (editBookmark edit_bm) >>= case _ of
Left affErr -> do Left affErr -> do
_apiError .= Just (printError affErr)
liftEffect $ log (printError affErr) liftEffect $ log (printError affErr)
Right { status: StatusCode s } | s >= 200 && s < 300 -> do Right { status: StatusCode s } | s >= 200 && s < 300 -> do
_bm .= edit_bm _bm .= edit_bm
@ -204,4 +215,5 @@ addbmark b' =
Nothing -> setHref org loc Nothing -> setHref org loc
_ -> liftEffect $ closeWindow =<< window _ -> liftEffect $ closeWindow =<< window
Right res -> do Right res -> do
_apiError .= Just (res.body)
liftEffect $ log (res.body) liftEffect $ log (res.body)

View file

@ -2,24 +2,29 @@ module Component.BMark where
import Prelude hiding (div) import Prelude hiding (div)
import Affjax (printError)
import Affjax.StatusCode (StatusCode(..))
import App (StarAction(..), destroy, editBookmark, markRead, toggleStar, lookupTitle) import App (StarAction(..), destroy, editBookmark, markRead, toggleStar, lookupTitle)
import Component.Markdown as Markdown import Component.Markdown as Markdown
import Data.Const (Const) import Data.Const (Const)
import Data.Either (Either(..))
import Data.Lens (Lens', lens, use, (%=), (.=)) import Data.Lens (Lens', lens, use, (%=), (.=))
import Data.Maybe (Maybe(..), fromMaybe, isJust) import Data.Maybe (Maybe(..), fromMaybe, isJust)
import Data.Monoid (guard) import Data.Monoid (guard)
import Data.Nullable (toMaybe) import Data.Nullable (toMaybe)
import Data.String (null, split, take, replaceAll) as S import Data.String (null, split, take, replaceAll) as S
import Data.String.Pattern (Pattern(..), Replacement(..)) import Data.String.Pattern (Pattern(..), Replacement(..))
import Type.Proxy (Proxy(..))
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Effect.Class.Console (log)
import Globals (app', setFocus, toLocaleDateString) import Globals (app', setFocus, toLocaleDateString)
import Halogen as H import Halogen as H
import Halogen.HTML (a, br_, button, div, div_, form, input, label, span, text, textarea) import Halogen.HTML (a, br_, button, div, div_, form, input, label, span, text, textarea)
import Halogen.HTML as HH import Halogen.HTML as HH
import Halogen.HTML.Events (onSubmit, onValueChange, onChecked, onClick) import Halogen.HTML.Events (onSubmit, onValueChange, onChecked, onClick)
import Halogen.HTML.Properties (ButtonType(..), InputType(..), autocomplete, checked, disabled, for, href, id_, name, required, rows, target, title, type_, value) import Halogen.HTML.Properties (ButtonType(..), InputType(..), autocomplete, checked, disabled, for, href, id, name, required, rows, target, title, type_, value)
import Model (Bookmark) import Model (Bookmark)
import Type.Proxy (Proxy(..))
import Util (attr, class_, fromNullableStr, ifElseH, whenH, whenA) import Util (attr, class_, fromNullableStr, ifElseH, whenH, whenA)
import Web.Event.Event (Event, preventDefault) import Web.Event.Event (Event, preventDefault)
@ -55,6 +60,7 @@ type BState =
, deleteAsk:: Boolean , deleteAsk:: Boolean
, edit :: Boolean , edit :: Boolean
, loading :: Boolean , loading :: Boolean
, apiError :: Maybe String
} }
_bm :: Lens' BState Bookmark _bm :: Lens' BState Bookmark
@ -66,6 +72,9 @@ _edit_bm = lens _.edit_bm (_ { edit_bm = _ })
_edit :: Lens' BState Boolean _edit :: Lens' BState Boolean
_edit = lens _.edit (_ { edit = _ }) _edit = lens _.edit (_ { edit = _ })
_apiError :: Lens' BState (Maybe String)
_apiError = lens _.apiError (_ { apiError = _ })
_markdown = Proxy :: Proxy "markdown" _markdown = Proxy :: Proxy "markdown"
type ChildSlots = type ChildSlots =
@ -88,11 +97,12 @@ bmark b' =
, deleteAsk: false , deleteAsk: false
, edit: false , edit: false
, loading: false , loading: false
, apiError: Nothing
} }
render :: BState -> H.ComponentHTML BAction ChildSlots Aff render :: BState -> H.ComponentHTML BAction ChildSlots Aff
render s@{ bm, edit_bm } = render s@{ bm, edit_bm, apiError } =
div [ id_ (show bm.bid) , class_ ("bookmark w-100 mw7 pa1 mb3" <> guard bm.private " private")] $ div [ id (show bm.bid) , class_ ("bookmark w-100 mw7 pa1 mb3" <> guard bm.private " private")] $
[ whenH app.dat.isowner [ whenH app.dat.isowner
star star
, ifElseH s.edit , ifElseH s.edit
@ -151,7 +161,9 @@ bmark b' =
display_edit _ = display_edit _ =
div [ class_ "edit_bookmark_form pa2 pt0 bg-white" ] $ div [ class_ "edit_bookmark_form pa2 pt0 bg-white" ] $
[ form [ onSubmit BEditSubmit ] [ whenH (isJust apiError)
(alert_notification (fromMaybe "" apiError))
, form [ onSubmit BEditSubmit ]
[ div_ [ text "url" ] [ div_ [ text "url" ]
, input [ type_ InputUrl , class_ "url w-100 mb2 pt1 edit_form_input" , required true , name "url" , input [ type_ InputUrl , class_ "url w-100 mb2 pt1 edit_form_input" , required true , name "url"
, value (edit_bm.url) , onValueChange (editField Eurl) ] , value (edit_bm.url) , onValueChange (editField Eurl) ]
@ -164,19 +176,19 @@ bmark b' =
, div_ [ text "description" ] , div_ [ text "description" ]
, textarea [ class_ "description w-100 mb1 pt1 edit_form_input" , name "description", rows 5 , textarea [ class_ "description w-100 mb1 pt1 edit_form_input" , name "description", rows 5
, value (edit_bm.description) , onValueChange (editField Edescription) ] , value (edit_bm.description) , onValueChange (editField Edescription) ]
, div [ id_ "tags_input_box"] , div [ id "tags_input_box"]
[ div_ [ text "tags" ] [ div_ [ text "tags" ]
, input [ id_ (tagid edit_bm), type_ InputText , class_ "tags w-100 mb1 pt1 edit_form_input" , name "tags" , input [ id (tagid edit_bm), type_ InputText , class_ "tags w-100 mb1 pt1 edit_form_input" , name "tags"
, autocomplete false, attr "autocapitalize" "off" , autocomplete false, attr "autocapitalize" "off"
, value (edit_bm.tags) , onValueChange (editField Etags) ] , value (edit_bm.tags) , onValueChange (editField Etags) ]
] ]
, div [ class_ "edit_form_checkboxes mv3"] , div [ class_ "edit_form_checkboxes mv3"]
[ input [ type_ InputCheckbox , class_ "private pointer" , id_ "edit_private", name "private" [ input [ type_ InputCheckbox , class_ "private pointer" , id "edit_private", name "private"
, checked (edit_bm.private) , onChecked (editField Eprivate) ] , checked (edit_bm.private) , onChecked (editField Eprivate) ]
, text " " , text " "
, label [ for "edit_private" , class_ "mr2" ] [ text "private" ] , label [ for "edit_private" , class_ "mr2" ] [ text "private" ]
, text " " , text " "
, input [ type_ InputCheckbox , class_ "toread pointer" , id_ "edit_toread", name "toread" , input [ type_ InputCheckbox , class_ "toread pointer" , id "edit_toread", name "toread"
, checked (edit_bm.toread) , onChecked (editField Etoread) ] , checked (edit_bm.toread) , onChecked (editField Etoread) ]
, text " " , text " "
, label [ for "edit_toread" ] [ text "to-read" ] , label [ for "edit_toread" ] [ text "to-read" ]
@ -188,6 +200,8 @@ bmark b' =
] ]
] ]
alert_notification alert_text _ =
div [ class_ "alert alert-err" ] [ text alert_text ]
editField :: forall a. (a -> EditField) -> a -> BAction editField :: forall a. (a -> EditField) -> a -> BAction
editField f = BEditField <<< f editField f = BEditField <<< f
@ -228,6 +242,7 @@ bmark b' =
bm <- use _bm bm <- use _bm
_edit_bm .= bm _edit_bm .= bm
_edit .= e _edit .= e
_apiError .= Nothing
H.liftEffect $ H.liftEffect $
when e when e
(setFocus (tagid bm)) (setFocus (tagid bm))
@ -256,7 +271,15 @@ bmark b' =
handleAction (BEditSubmit e) = do handleAction (BEditSubmit e) = do
H.liftEffect (preventDefault e) H.liftEffect (preventDefault e)
edit_bm <- use _edit_bm edit_bm <- use _edit_bm
_apiError .= Nothing
let edit_bm' = edit_bm { tags = S.replaceAll (Pattern ",") (Replacement " ") edit_bm.tags } let edit_bm' = edit_bm { tags = S.replaceAll (Pattern ",") (Replacement " ") edit_bm.tags }
void $ H.liftAff (editBookmark edit_bm') H.liftAff (editBookmark edit_bm') >>= case _ of
_bm .= edit_bm' Left affErr -> do
_edit .= false _apiError .= Just (printError affErr)
liftEffect $ log (printError affErr)
Right { status: StatusCode s } | s >= 200 && s < 300 -> do
_bm .= edit_bm'
_edit .= false
Right res -> do
_apiError .= Just (res.body)
liftEffect $ log (res.body)

View file

@ -12,7 +12,7 @@ import Globals (app', mmoment8601)
import Halogen as H import Halogen as H
import Halogen.HTML (a, br_, div, text) import Halogen.HTML (a, br_, div, text)
import Halogen.HTML as HH import Halogen.HTML as HH
import Halogen.HTML.Properties (href, id_, title) import Halogen.HTML.Properties (href, id, title)
import Model (Note, NoteSlug) import Model (Note, NoteSlug)
import Util (class_, fromNullableStr) import Util (class_, fromNullableStr)
@ -49,7 +49,7 @@ nlist st' =
HH.div_ (map renderNote notes) HH.div_ (map renderNote notes)
where where
renderNote note = renderNote note =
div [ id_ (show note.id) div [ id (show note.id)
, class_ ("note w-100 mw7 pa1 mb2" , class_ ("note w-100 mw7 pa1 mb2"
<> if note.shared then "" else " private")] $ <> if note.shared then "" else " private")] $
[ div [ class_ "display" ] $ [ div [ class_ "display" ] $

View file

@ -2,12 +2,15 @@ module Component.NNote where
import Prelude hiding (div) import Prelude hiding (div)
import Affjax (printError)
import Affjax.StatusCode (StatusCode(..))
import App (destroyNote, editNote) import App (destroyNote, editNote)
import Component.Markdown as Markdown import Component.Markdown as Markdown
import Data.Array (drop, foldMap) import Data.Array (drop, foldMap)
import Data.Either (Either(..))
import Data.Foldable (for_) import Data.Foldable (for_)
import Data.Lens (Lens', lens, use, (%=), (.=)) import Data.Lens (Lens', lens, use, (%=), (.=))
import Data.Maybe (Maybe(..), isJust, maybe) import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe)
import Data.Monoid (guard) import Data.Monoid (guard)
import Data.String (null, split) as S import Data.String (null, split) as S
import Data.String (null, stripPrefix) import Data.String (null, stripPrefix)
@ -15,12 +18,13 @@ import Data.String.Pattern (Pattern(..))
import Data.Tuple (fst, snd) import Data.Tuple (fst, snd)
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Console (log)
import Globals (app', mmoment8601, setFocus, closeWindow) import Globals (app', mmoment8601, setFocus, closeWindow)
import Halogen as H import Halogen as H
import Halogen.HTML (br_, button, div, form, input, label, p, span, text, textarea) import Halogen.HTML (br_, button, div, form, input, label, p, span, text, textarea)
import Halogen.HTML as HH import Halogen.HTML as HH
import Halogen.HTML.Events (onChecked, onClick, onSubmit, onValueChange) import Halogen.HTML.Events (onChecked, onClick, onSubmit, onValueChange)
import Halogen.HTML.Properties (ButtonType(..), InputType(..), autofocus, checked, for, id_, name, rows, title, type_, value) import Halogen.HTML.Properties (ButtonType(..), InputType(..), autofocus, checked, for, id, name, rows, title, type_, value)
import Model (Note) import Model (Note)
import Type.Proxy (Proxy(..)) import Type.Proxy (Proxy(..))
import Util (_curQuerystring, _doc, _loc, _lookupQueryStringValue, class_, fromNullableStr, ifElseH, whenH) import Util (_curQuerystring, _doc, _loc, _lookupQueryStringValue, class_, fromNullableStr, ifElseH, whenH)
@ -43,6 +47,7 @@ type NState =
, deleteAsk :: Boolean , deleteAsk :: Boolean
, edit :: Boolean , edit :: Boolean
, destroyed :: Boolean , destroyed :: Boolean
, apiError :: Maybe String
} }
_note :: Lens' NState Note _note :: Lens' NState Note
@ -54,6 +59,9 @@ _edit_note = lens _.edit_note (_ { edit_note = _ })
_edit :: Lens' NState Boolean _edit :: Lens' NState Boolean
_edit = lens _.edit (_ { edit = _ }) _edit = lens _.edit (_ { edit = _ })
_apiError :: Lens' NState (Maybe String)
_apiError = lens _.apiError (_ { apiError = _ })
-- | FormField Edits -- | FormField Edits
data EditField data EditField
= Etitle String = Etitle String
@ -83,10 +91,11 @@ nnote st' =
, deleteAsk: false , deleteAsk: false
, edit: note'.id <= 0 , edit: note'.id <= 0
, destroyed: false , destroyed: false
, apiError: Nothing
} }
render :: NState -> H.ComponentHTML NAction ChildSlots Aff render :: NState -> H.ComponentHTML NAction ChildSlots Aff
render st@{ note, edit_note } = render st@{ note, edit_note, apiError } =
ifElseH st.destroyed ifElseH st.destroyed
display_destroyed display_destroyed
(const (ifElseH st.edit (const (ifElseH st.edit
@ -95,7 +104,7 @@ nnote st' =
where where
renderNote _ = renderNote _ =
div [ id_ (show note.id) , class_ ("note w-100 mw7 pa1 mb2")] $ div [ id (show note.id) , class_ ("note w-100 mw7 pa1 mb2")] $
[ div [ class_ "display" ] $ [ div [ class_ "display" ] $
[ div [ class_ ("link f5 lh-title")] [ div [ class_ ("link f5 lh-title")]
[ text $ if S.null note.title then "[no title]" else note.title ] [ text $ if S.null note.title then "[no title]" else note.title ]
@ -127,24 +136,26 @@ nnote st' =
renderNote_edit _ = renderNote_edit _ =
form [ onSubmit NEditSubmit ] form [ onSubmit NEditSubmit ]
[ p [ class_ "mt2 mb1"] [ text "title:" ] [ whenH (isJust apiError)
(alert_notification (fromMaybe "" apiError))
, p [ class_ "mt2 mb1"] [ text "title:" ]
, input [ type_ InputText , class_ "title w-100 mb1 pt1 edit_form_input" , name "title" , input [ type_ InputText , class_ "title w-100 mb1 pt1 edit_form_input" , name "title"
, value (edit_note.title) , onValueChange (editField Etitle), autofocus (null edit_note.title) , value (edit_note.title) , onValueChange (editField Etitle), autofocus (null edit_note.title)
] ]
, br_ , br_
, p [ class_ "mt2 mb1"] [ text "description:" ] , p [ class_ "mt2 mb1"] [ text "description:" ]
, textarea [ id_ (notetextid edit_note), class_ "description w-100 mb1 pt1 edit_form_input" , name "text", rows 25 , textarea [ id (notetextid edit_note), class_ "description w-100 mb1 pt1 edit_form_input" , name "text", rows 25
, value (edit_note.text) , onValueChange (editField Etext) , value (edit_note.text) , onValueChange (editField Etext)
] ]
, div [ class_ "edit_form_checkboxes mb3"] , div [ class_ "edit_form_checkboxes mb3"]
[ input [ type_ InputCheckbox , class_ "is-markdown pointer" , id_ "edit_ismarkdown", name "ismarkdown" [ input [ type_ InputCheckbox , class_ "is-markdown pointer" , id "edit_ismarkdown", name "ismarkdown"
, checked (edit_note.isMarkdown) , onChecked (editField EisMarkdown) ] , checked (edit_note.isMarkdown) , onChecked (editField EisMarkdown) ]
, text " " , text " "
, label [ for "edit_ismarkdown" , class_ "mr2" ] [ text "use markdown?" ] , label [ for "edit_ismarkdown" , class_ "mr2" ] [ text "use markdown?" ]
, br_ , br_
] ]
, div [ class_ "edit_form_checkboxes mb3"] , div [ class_ "edit_form_checkboxes mb3"]
[ input [ type_ InputCheckbox , class_ "is-markdown pointer" , id_ "edit_shared", name "shared" [ input [ type_ InputCheckbox , class_ "is-markdown pointer" , id "edit_shared", name "shared"
, checked (edit_note.shared) , onChecked (editField Eshared) ] , checked (edit_note.shared) , onChecked (editField Eshared) ]
, text " " , text " "
, label [ for "edit_shared" , class_ "mr2" ] [ text "public?" ] , label [ for "edit_shared" , class_ "mr2" ] [ text "public?" ]
@ -163,6 +174,9 @@ nnote st' =
display_destroyed _ = p [ class_ "red"] [text "you killed this note"] display_destroyed _ = p [ class_ "red"] [text "you killed this note"]
alert_notification alert_text _ =
div [ class_ "alert alert-err" ] [ text alert_text ]
mmoment n = mmoment8601 n.created mmoment n = mmoment8601 n.created
editField :: forall a. (a -> EditField) -> a -> NAction editField :: forall a. (a -> EditField) -> a -> NAction
editField f = NEditField <<< f editField f = NEditField <<< f
@ -209,21 +223,28 @@ nnote st' =
handleAction (NEditSubmit e) = do handleAction (NEditSubmit e) = do
H.liftEffect (preventDefault e) H.liftEffect (preventDefault e)
edit_note <- use _edit_note edit_note <- use _edit_note
res' <- H.liftAff (editNote edit_note) _apiError .= Nothing
for_ res' \_ -> do H.liftAff (editNote edit_note) >>= case _ of
qs <- liftEffect _curQuerystring Left affErr -> do
doc <- liftEffect $ _doc _apiError .= Just (printError affErr)
ref <- liftEffect $ referrer doc liftEffect $ log (printError affErr)
loc <- liftEffect $ _loc Right { status: StatusCode s } | s >= 200 && s < 300 -> do
org <- liftEffect $ origin loc qs <- liftEffect _curQuerystring
case _lookupQueryStringValue qs "next" of doc <- liftEffect $ _doc
Just "closeWindow" -> liftEffect $ closeWindow =<< window ref <- liftEffect $ referrer doc
Just "back" -> liftEffect $ loc <- liftEffect $ _loc
if isJust (stripPrefix (Pattern org) ref) org <- liftEffect $ origin loc
then setHref ref loc case _lookupQueryStringValue qs "next" of
else setHref org loc Just "closeWindow" -> liftEffect $ closeWindow =<< window
_ -> if (edit_note.id == 0) Just "back" -> liftEffect $
then liftEffect $ setHref (fromNullableStr app.noteR) =<< _loc if isJust (stripPrefix (Pattern org) ref)
else do then setHref ref loc
_note .= edit_note else setHref org loc
_edit .= false _ -> if (edit_note.id == 0)
then liftEffect $ setHref (fromNullableStr app.noteR) =<< _loc
else do
_note .= edit_note
_edit .= false
Right res -> do
_apiError .= Just (res.body)
liftEffect $ log (res.body)

View file

@ -151,6 +151,11 @@ label {
.alert { .alert {
background: #ced; background: #ced;
border: 1px solid #acc; border: 1px solid #acc;
margin-bottom: 5px;
padding: 2px;
}
.alert.alert-err {
background-color: #ffdfdf
} }
.edit_bookmark_form { .edit_bookmark_form {

View file

@ -25,6 +25,9 @@ textarea {
#addForm .alert { #addForm .alert {
margin-top: -6px; margin-top: -6px;
} }
.alert.alert-err {
background-color: #ffdfdf
}
form label { form label {
margin: 0; margin: 0;
vertical-align: middle; vertical-align: middle;

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.