add forms
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
#ifndef ghcjs_HOST_OS
|
#ifndef ghcjs_HOST_OS
|
||||||
|
import Data.Maybe
|
||||||
import Data.String
|
import Data.String
|
||||||
import Language.Javascript.JSaddle.Warp as JSaddle
|
import Language.Javascript.JSaddle.Warp as JSaddle
|
||||||
import Network.HTTP.Simple
|
import Network.HTTP.Simple
|
||||||
@@ -13,10 +14,14 @@ import Miso.String qualified as J
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
import Data.Aeson qualified as A
|
import Data.Aeson qualified as A
|
||||||
|
import Data.Aeson.Key qualified as AK
|
||||||
|
import Data.Aeson.KeyMap qualified as AM
|
||||||
import Data.ByteString.Lazy.Char8 qualified as LB
|
import Data.ByteString.Lazy.Char8 qualified as LB
|
||||||
import Data.Default
|
import Data.Default
|
||||||
import Data.Function
|
import Data.Function
|
||||||
|
import Data.List
|
||||||
import Data.Map qualified as M
|
import Data.Map qualified as M
|
||||||
|
import Data.Text qualified as T
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
import Miso
|
import Miso
|
||||||
import Miso.String (toMisoString)
|
import Miso.String (toMisoString)
|
||||||
@@ -66,6 +71,8 @@ data Action
|
|||||||
| SetSchema (Either String Schema)
|
| SetSchema (Either String Schema)
|
||||||
| FetchPosts
|
| FetchPosts
|
||||||
| SetPosts (Either String [A.Value])
|
| SetPosts (Either String [A.Value])
|
||||||
|
| FormChanged A.Value
|
||||||
|
| FormSubmitted A.Value
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
#ifndef ghcjs_HOST_OS
|
#ifndef ghcjs_HOST_OS
|
||||||
@@ -103,6 +110,8 @@ updateModel action m =
|
|||||||
let setPosts :: Either String [A.Value] -> Model -> Model
|
let setPosts :: Either String [A.Value] -> Model -> Model
|
||||||
setPosts posts m = m {posts = Just posts}
|
setPosts posts m = m {posts = Just posts}
|
||||||
in noEff (setPosts posts m)
|
in noEff (setPosts posts m)
|
||||||
|
FormChanged _ -> noEff m
|
||||||
|
FormSubmitted _ -> noEff m
|
||||||
|
|
||||||
fetchSchema :: JSM (Either String Schema)
|
fetchSchema :: JSM (Either String Schema)
|
||||||
fetchSchema =
|
fetchSchema =
|
||||||
@@ -149,7 +158,17 @@ viewModel :: Model -> View Action
|
|||||||
viewModel model =
|
viewModel model =
|
||||||
div_ [] $
|
div_ [] $
|
||||||
[ maybe (text "..") (either err viewSchema) model.schema,
|
[ maybe (text "..") (either err viewSchema) model.schema,
|
||||||
maybe (text "..") (either err viewPosts) model.posts
|
maybe (text "..") (either err viewPosts) model.posts,
|
||||||
|
maybe
|
||||||
|
(text "..")
|
||||||
|
( either
|
||||||
|
err
|
||||||
|
( fmap (either FormChanged FormSubmitted)
|
||||||
|
. flip viewForm (A.Object AM.empty)
|
||||||
|
. schemaForm
|
||||||
|
)
|
||||||
|
)
|
||||||
|
model.schema
|
||||||
]
|
]
|
||||||
|
|
||||||
err :: String -> View Action
|
err :: String -> View Action
|
||||||
@@ -169,6 +188,117 @@ viewSchema schema =
|
|||||||
)
|
)
|
||||||
<$> (M.toList properties)
|
<$> (M.toList properties)
|
||||||
|
|
||||||
|
schemaForm :: Schema -> Form A.Value A.Value
|
||||||
|
schemaForm schema =
|
||||||
|
mapOutput mergeJson . sequence $
|
||||||
|
case schema.type_ of
|
||||||
|
Object properties ->
|
||||||
|
( \(AK.fromString -> k, "string") ->
|
||||||
|
mapOutput (A.Object . AM.singleton k) $
|
||||||
|
mapValues (getO k) (setO k) $
|
||||||
|
jsonString (AK.toString k)
|
||||||
|
)
|
||||||
|
<$> (M.toList properties)
|
||||||
|
|
||||||
|
mergeJson :: [A.Value] -> A.Value
|
||||||
|
mergeJson = foldl' mergeObject (A.Object AM.empty)
|
||||||
|
|
||||||
|
mergeObject :: A.Value -> A.Value -> A.Value
|
||||||
|
mergeObject (A.Object kvs) (A.Object kvs') = A.Object (AM.union kvs kvs')
|
||||||
|
|
||||||
|
fromJson :: A.Value -> T.Text
|
||||||
|
fromJson (A.String x) = x
|
||||||
|
fromJson _ = ""
|
||||||
|
|
||||||
|
toJson :: T.Text -> A.Value -> A.Value
|
||||||
|
toJson x _ = A.String x
|
||||||
|
|
||||||
|
getO :: AK.Key -> A.Value -> A.Value
|
||||||
|
getO k (A.Object kvs) = fromMaybe A.Null (AM.lookup k kvs)
|
||||||
|
|
||||||
|
setO :: AK.Key -> A.Value -> A.Value -> A.Value
|
||||||
|
setO k v (A.Object kvs) = A.Object (AM.insert k v kvs)
|
||||||
|
|
||||||
|
data Form i o = Form
|
||||||
|
{ view :: i -> [View i],
|
||||||
|
fill :: i -> Either String o
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Functor (Form i) where
|
||||||
|
fmap f (Form {view, fill}) =
|
||||||
|
Form
|
||||||
|
{ fill = fmap f . fill,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Applicative (Form i) where
|
||||||
|
pure x =
|
||||||
|
Form
|
||||||
|
{ view = const [],
|
||||||
|
fill = const (Right x)
|
||||||
|
}
|
||||||
|
Form {view = viewF, fill = fillF} <*> Form {view = viewX, fill = fillX} =
|
||||||
|
Form
|
||||||
|
{ view = \i ->
|
||||||
|
let f = viewF i
|
||||||
|
x = viewX i
|
||||||
|
in f <> x,
|
||||||
|
fill = \i ->
|
||||||
|
let f = fillF i
|
||||||
|
x = fillX i
|
||||||
|
in ($) <$> f <*> x
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Monad (Form i) where
|
||||||
|
(Form {view = viewM, fill = fillM}) >>= mkF =
|
||||||
|
Form
|
||||||
|
{ view = \i ->
|
||||||
|
viewM i
|
||||||
|
<> case fillM i of
|
||||||
|
Right x -> (mkF x).view i
|
||||||
|
Left _ -> [],
|
||||||
|
fill = \i -> case fillM i of
|
||||||
|
Right x -> (mkF x).fill i
|
||||||
|
Left e -> Left e
|
||||||
|
}
|
||||||
|
|
||||||
|
mapValues :: (i' -> i) -> (i -> i' -> i') -> Form i o -> Form i' o
|
||||||
|
mapValues get set (Form {view, fill}) =
|
||||||
|
Form
|
||||||
|
{ view = \i -> fmap (flip set i) <$> view (get i),
|
||||||
|
fill = fill . get
|
||||||
|
}
|
||||||
|
|
||||||
|
mapOutput :: (o -> o') -> Form i o -> Form i o'
|
||||||
|
mapOutput = fmap
|
||||||
|
|
||||||
|
viewForm :: Form i o -> i -> View (Either i o)
|
||||||
|
viewForm (Form {view}) i =
|
||||||
|
div_ [] $
|
||||||
|
(fmap Left <$> view i)
|
||||||
|
<> [ button_ [type_ "submit"] [text "submit"]
|
||||||
|
]
|
||||||
|
|
||||||
|
jsonString :: String -> Form A.Value A.Value
|
||||||
|
jsonString = mapOutput A.String . mapValues fromJson toJson . string
|
||||||
|
|
||||||
|
string :: String -> Form T.Text T.Text
|
||||||
|
string label =
|
||||||
|
Form
|
||||||
|
{ view = \i ->
|
||||||
|
[ div_ [] $
|
||||||
|
[ label_ [] $
|
||||||
|
[ text (toMisoString label),
|
||||||
|
input_
|
||||||
|
[ type_ "text",
|
||||||
|
value_ (toMisoString i)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
fill = \i -> Right i
|
||||||
|
}
|
||||||
|
|
||||||
viewPosts :: [A.Value] -> View Action
|
viewPosts :: [A.Value] -> View Action
|
||||||
viewPosts posts = ol_ [] (viewPost <$> posts)
|
viewPosts posts = ol_ [] (viewPost <$> posts)
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ executable frontend
|
|||||||
default-extensions:
|
default-extensions:
|
||||||
CPP OverloadedStrings RecordWildCards DeriveAnyClass
|
CPP OverloadedStrings RecordWildCards DeriveAnyClass
|
||||||
DuplicateRecordFields LambdaCase OverloadedRecordDot
|
DuplicateRecordFields LambdaCase OverloadedRecordDot
|
||||||
NoFieldSelectors
|
NoFieldSelectors ViewPatterns
|
||||||
|
|
||||||
ghc-options:
|
ghc-options:
|
||||||
-Wall -fno-warn-name-shadowing -fno-warn-ambiguous-fields
|
-Wall -fno-warn-name-shadowing -fno-warn-ambiguous-fields
|
||||||
-fno-warn-incomplete-patterns -fno-warn-orphans
|
-fno-warn-incomplete-patterns -fno-warn-orphans
|
||||||
|
-fno-warn-incomplete-uni-patterns
|
||||||
|
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson,
|
aeson,
|
||||||
|
|||||||
Reference in New Issue
Block a user