248 lines
8.8 KiB
Plaintext
248 lines
8.8 KiB
Plaintext
module Component.BMark where
|
||
|
||
import Prelude hiding (div)
|
||
|
||
import App (StarAction(..), destroy, editBookmark, markRead, toggleStar)
|
||
import Data.Array (drop, foldMap)
|
||
import Data.Lens (Lens', lens, use, (%=), (.=))
|
||
import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe)
|
||
import Data.Monoid (guard)
|
||
import Data.Nullable (toMaybe)
|
||
import Data.String (null, split, take) as S
|
||
import Data.String.Pattern (Pattern(..))
|
||
import Data.Tuple (fst, snd)
|
||
import Effect.Aff (Aff)
|
||
import Globals (app', mmoment8601)
|
||
import Halogen as H
|
||
import Halogen.HTML (HTML, a, br_, button, div, div_, form, input, label, span, text, textarea)
|
||
import Halogen.HTML.Events (onSubmit, onValueChange, onChecked, onClick)
|
||
import Halogen.HTML.Events as HE
|
||
import Halogen.HTML.Properties (ButtonType(..), InputType(..), autocomplete, checked, for, href, id_, name, required, rows, target, title, type_, value)
|
||
import Model (Bookmark)
|
||
import Util (class_, attr, fromNullableStr)
|
||
import Web.Event.Event (Event, preventDefault)
|
||
|
||
-- | UI Events
|
||
data BQuery a
|
||
= BStar Boolean a
|
||
| BDeleteAsk Boolean a
|
||
| BDestroy a
|
||
| BEdit Boolean a
|
||
| BEditField EditField a
|
||
| BEditSubmit Event a
|
||
| BMarkRead a
|
||
|
||
-- | FormField Edits
|
||
data EditField
|
||
= Eurl String
|
||
| Etitle String
|
||
| Edescription String
|
||
| Etags String
|
||
| Eprivate Boolean
|
||
| Etoread Boolean
|
||
|
||
-- | Messages to parent
|
||
data BMessage
|
||
= BNotifyRemove
|
||
|
||
type BState =
|
||
{ bm :: Bookmark
|
||
, edit_bm :: Bookmark
|
||
, deleteAsk:: Boolean
|
||
, edit :: Boolean
|
||
}
|
||
|
||
_bm :: Lens' BState Bookmark
|
||
_bm = lens _.bm (_ { bm = _ })
|
||
|
||
_edit_bm :: Lens' BState Bookmark
|
||
_edit_bm = lens _.edit_bm (_ { edit_bm = _ })
|
||
|
||
_edit :: Lens' BState Boolean
|
||
_edit = lens _.edit (_ { edit = _ })
|
||
|
||
bmark :: Bookmark -> H.Component HTML BQuery Unit BMessage Aff
|
||
bmark b' =
|
||
H.component
|
||
{ initialState: const (mkState b')
|
||
, render
|
||
, eval
|
||
, receiver: const Nothing
|
||
}
|
||
where
|
||
app = app' unit
|
||
|
||
mkState b =
|
||
{ bm: b
|
||
, edit_bm: b
|
||
, deleteAsk: false
|
||
, edit: false
|
||
}
|
||
|
||
render :: BState -> H.ComponentHTML BQuery
|
||
render s@{ bm, edit_bm } =
|
||
div [ id_ (show bm.bid) , class_ ("bookmark w-100 mw7 pa1 mb3" <> guard bm.private " private")] $
|
||
star <>
|
||
if s.edit
|
||
then display_edit
|
||
else display
|
||
where
|
||
|
||
star =
|
||
guard app.dat.isowner
|
||
[ div [ class_ ("star fl pointer" <> guard bm.selected " selected") ]
|
||
[ button [ class_ "moon-gray", onClick (HE.input_ (BStar (not bm.selected))) ] [ text "✭" ] ]
|
||
]
|
||
|
||
display =
|
||
[ div [ class_ "display" ] $
|
||
[ a [ href bm.url, target "_blank", class_ ("link f5 lh-title" <> guard bm.toread " unread")]
|
||
[ text $ if S.null bm.title then "[no title]" else bm.title ]
|
||
, br_
|
||
, a [ href bm.url , class_ "link f7 gray hover-blue" ] [ text bm.url ]
|
||
, a [ href (fromMaybe ("http://archive.is/" <> bm.url) (toMaybe bm.archiveUrl))
|
||
, class_ ("link f7 gray hover-blue ml2" <> (guard (isJust (toMaybe bm.archiveUrl)) " green"))
|
||
, target "_blank", title "archive link"]
|
||
[ if isJust (toMaybe bm.archiveUrl) then text "☑" else text "☐" ]
|
||
, br_
|
||
--
|
||
, div [ class_ "description mt1 mid-gray" ] (toTextarea bm.description)
|
||
, div [ class_ "tags" ] $
|
||
guard (not (S.null bm.tags))
|
||
map (\tag -> a [ class_ ("link tag mr1" <> guard (S.take 1 tag == ".") " private")
|
||
, href (linkToFilterTag tag) ]
|
||
[ text tag ])
|
||
(S.split (Pattern " ") bm.tags)
|
||
, a [ class_ "link f7 dib gray w4", title (maybe bm.time snd mmoment) , href (linkToFilterSingle bm.slug) ]
|
||
[ text (maybe " " fst mmoment) ]
|
||
]
|
||
<> links
|
||
]
|
||
|
||
display_edit =
|
||
[ div [ class_ "edit_bookmark_form pa2 pt0 bg-white" ] $
|
||
[ form [ onSubmit (HE.input BEditSubmit) ]
|
||
[ div_ [ text "url" ]
|
||
, input [ type_ InputUrl , class_ "url w-100 mb2 pt1 f7 edit_form_input" , required true , name "url"
|
||
, value (edit_bm.url) , onValueChange (editField Eurl) ]
|
||
, br_
|
||
, div_ [ text "title" ]
|
||
, input [ type_ InputText , class_ "title w-100 mb2 pt1 f7 edit_form_input" , name "title"
|
||
, value (edit_bm.title) , onValueChange (editField Etitle) ]
|
||
, br_
|
||
, div_ [ text "description" ]
|
||
, textarea [ class_ "description w-100 mb1 pt1 f7 edit_form_input" , name "description", rows 5
|
||
, value (edit_bm.description) , onValueChange (editField Edescription) ]
|
||
, br_
|
||
, div [ id_ "tags_input_box"]
|
||
[ div_ [ text "tags" ]
|
||
, input [ type_ InputText , class_ "tags w-100 mb1 pt1 f7 edit_form_input" , name "tags"
|
||
, autocomplete false, attr "autocapitalize" "off"
|
||
, value (edit_bm.tags) , onValueChange (editField Etags) ]
|
||
, br_
|
||
]
|
||
, div [ class_ "edit_form_checkboxes mv3"]
|
||
[ input [ type_ InputCheckbox , class_ "private pointer" , id_ "edit_private", name "private"
|
||
, checked (edit_bm.private) , onChecked (editField Eprivate) ]
|
||
, text " "
|
||
, label [ for "edit_private" , class_ "mr2" ] [ text "private" ]
|
||
, text " "
|
||
, input [ type_ InputCheckbox , class_ "toread pointer" , id_ "edit_toread", name "toread"
|
||
, checked (edit_bm.toread) , onChecked (editField Etoread) ]
|
||
, text " "
|
||
, label [ for "edit_toread" ] [ text "to-read" ]
|
||
, br_
|
||
]
|
||
, input [ type_ InputSubmit , class_ "mr1 pv1 ph2 dark-gray ba b--moon-gray bg-near-white pointer rdim" , value "save" ]
|
||
, text " "
|
||
, input [ type_ InputReset , class_ "pv1 ph2 dark-gray ba b--moon-gray bg-near-white pointer rdim" , value "cancel"
|
||
, onClick (HE.input_ (BEdit false)) ]
|
||
]
|
||
]
|
||
]
|
||
|
||
links =
|
||
guard app.dat.isowner
|
||
[ div [ class_ "edit_links di" ]
|
||
[ button [ type_ ButtonButton, onClick (HE.input_ (BEdit true)), class_ "edit light-silver hover-blue" ] [ text "edit " ]
|
||
, div [ class_ "delete_link di" ]
|
||
[ button [ type_ ButtonButton, onClick (HE.input_ (BDeleteAsk true)), class_ ("delete light-silver hover-blue" <> guard s.deleteAsk " dn") ] [ text "delete" ]
|
||
, span ([ class_ ("confirm red" <> guard (not s.deleteAsk) " dn") ] )
|
||
[ button [ type_ ButtonButton, onClick (HE.input_ (BDeleteAsk false))] [ text "cancel / " ]
|
||
, button [ type_ ButtonButton, onClick (HE.input_ BDestroy), class_ "red" ] [ text "destroy" ]
|
||
]
|
||
]
|
||
]
|
||
, div [ class_ "read di" ] $
|
||
guard bm.toread
|
||
[ text " "
|
||
, button [ onClick (HE.input_ BMarkRead), class_ "mark_read" ] [ text "mark as read"]
|
||
]
|
||
]
|
||
|
||
editField :: forall a. (a -> EditField) -> a -> Maybe (BQuery Unit)
|
||
editField f = HE.input BEditField <<< f
|
||
linkToFilterSingle slug = fromNullableStr app.userR <> "/b:" <> slug
|
||
linkToFilterTag tag = fromNullableStr app.userR <> "/t:" <> tag
|
||
mmoment = mmoment8601 bm.time
|
||
toTextarea input =
|
||
S.split (Pattern "\n") input
|
||
# foldMap (\x -> [br_, text x])
|
||
# drop 1
|
||
|
||
eval :: BQuery ~> H.ComponentDSL BState BQuery BMessage Aff
|
||
|
||
-- | Star
|
||
eval (BStar e next) = do
|
||
bm <- use _bm
|
||
H.liftAff (toggleStar bm.bid (if e then Star else UnStar))
|
||
_bm %= _ { selected = e }
|
||
_edit_bm %= _ { selected = e }
|
||
pure next
|
||
|
||
-- | Delete
|
||
eval (BDeleteAsk e next) = do
|
||
H.modify_ (_ { deleteAsk = e })
|
||
pure next
|
||
|
||
-- | Destroy
|
||
eval (BDestroy next) = do
|
||
bm <- use _bm
|
||
void $ H.liftAff (destroy bm.bid)
|
||
H.raise BNotifyRemove
|
||
pure next
|
||
|
||
-- | Mark Read
|
||
eval (BMarkRead next) = do
|
||
bm <- use _bm
|
||
void (H.liftAff (markRead bm.bid))
|
||
_bm %= _ { toread = false }
|
||
pure next
|
||
|
||
-- | Start/Stop Editing
|
||
eval (BEdit e next) = do
|
||
bm <- use _bm
|
||
_edit_bm .= bm
|
||
_edit .= e
|
||
pure next
|
||
|
||
-- | Update Form Field
|
||
eval (BEditField f next) = do
|
||
_edit_bm %= case f of
|
||
Eurl e -> _ { url = e }
|
||
Etitle e -> _ { title = e }
|
||
Edescription e -> _ { description = e }
|
||
Etags e -> _ { tags = e }
|
||
Eprivate e -> _ { private = e }
|
||
Etoread e -> _ { toread = e }
|
||
pure next
|
||
|
||
-- | Submit
|
||
eval (BEditSubmit e next) = do
|
||
H.liftEffect (preventDefault e)
|
||
edit_bm <- use _edit_bm
|
||
void $ H.liftAff (editBookmark edit_bm)
|
||
_bm .= edit_bm
|
||
_edit .= false
|
||
pure next
|