Compare commits
2 Commits
41463c1ac7
...
c9406d40a8
Author | SHA1 | Date | |
---|---|---|---|
|
c9406d40a8 | ||
|
2f01c3509b |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
|||||||
devenv.local.nix
|
devenv.local.nix
|
||||||
db
|
db
|
||||||
datasets
|
datasets
|
||||||
|
elm-stuff
|
||||||
|
|
||||||
# direnv
|
# direnv
|
||||||
.direnv
|
.direnv
|
||||||
|
24
devenv.nix
24
devenv.nix
@ -4,7 +4,19 @@
|
|||||||
config,
|
config,
|
||||||
inputs,
|
inputs,
|
||||||
...
|
...
|
||||||
}: {
|
}: let
|
||||||
|
nsrc = pkgs.fetchFromGitHub {
|
||||||
|
owner = "elm-tooling";
|
||||||
|
repo = "elm-language-server";
|
||||||
|
rev = "0dc4076180fe7e758bed267a84911cc202011a13";
|
||||||
|
sha256 = "1xys9a468fy1vxlby8pmvnpv0kg5hr4956mdp2kclvs55k4j9y43";
|
||||||
|
};
|
||||||
|
elm-lsp =
|
||||||
|
lib.overrideDerivation pkgs.elmPackages.elm-language-server
|
||||||
|
(drv: {
|
||||||
|
src = nsrc;
|
||||||
|
});
|
||||||
|
in {
|
||||||
# https://devenv.sh/basics/
|
# https://devenv.sh/basics/
|
||||||
env.GREET = "devenv";
|
env.GREET = "devenv";
|
||||||
|
|
||||||
@ -13,12 +25,20 @@
|
|||||||
sqlite
|
sqlite
|
||||||
nodePackages.typescript-language-server
|
nodePackages.typescript-language-server
|
||||||
nodePackages.prettier
|
nodePackages.prettier
|
||||||
|
elm2nix
|
||||||
|
elmPackages.elm
|
||||||
|
elmPackages.elm-format
|
||||||
|
elmPackages.elm-review
|
||||||
|
elmPackages.elm-test-rs
|
||||||
|
elmPackages.elm-land
|
||||||
|
elm-lsp
|
||||||
|
# elmPackages.lamdera # lol
|
||||||
];
|
];
|
||||||
|
|
||||||
# https://devenv.sh/languages/
|
# https://devenv.sh/languages/
|
||||||
# languages.rust.enable = true;
|
# languages.rust.enable = true;
|
||||||
languages = {
|
languages = {
|
||||||
elm.enable = true;
|
# elm.enable = true;
|
||||||
javascript = {
|
javascript = {
|
||||||
enable = true;
|
enable = true;
|
||||||
bun.enable = true;
|
bun.enable = true;
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
"elm/html": "1.0.0",
|
"elm/html": "1.0.0",
|
||||||
"elm/http": "2.0.0",
|
"elm/http": "2.0.0",
|
||||||
"elm/json": "1.1.3",
|
"elm/json": "1.1.3",
|
||||||
|
"elm/url": "1.0.0",
|
||||||
|
"elm-community/list-extra": "8.7.0",
|
||||||
"mdgriffith/elm-ui": "1.1.8"
|
"mdgriffith/elm-ui": "1.1.8"
|
||||||
},
|
},
|
||||||
"indirect": {
|
"indirect": {
|
||||||
"elm/bytes": "1.0.8",
|
"elm/bytes": "1.0.8",
|
||||||
"elm/file": "1.0.5",
|
"elm/file": "1.0.5",
|
||||||
"elm/time": "1.0.0",
|
"elm/time": "1.0.0",
|
||||||
"elm/url": "1.0.0",
|
|
||||||
"elm/virtual-dom": "1.0.3"
|
"elm/virtual-dom": "1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,46 +1,22 @@
|
|||||||
module Api exposing (Card, Lesson, Lessons, ServerResponse(..), fetchLessons)
|
module Api exposing (addUser, fetchLesson, fetchLessons)
|
||||||
|
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
import Http
|
import Http
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
|
import Json.Encode as Encode
|
||||||
|
import Types exposing (Card, Lesson, Lessons, Msg(..), ServerResponse(..))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- data types
|
-- data types
|
||||||
|
|
||||||
|
|
||||||
type alias Card =
|
|
||||||
{ text : String
|
|
||||||
, note : Maybe String
|
|
||||||
, id : Int
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type alias Lesson =
|
|
||||||
{ text : String
|
|
||||||
, id : Int
|
|
||||||
, cards : List Card
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type alias Lessons =
|
|
||||||
Dict Int Lesson
|
|
||||||
|
|
||||||
|
|
||||||
type ServerResponse
|
|
||||||
= OkResponse Lessons
|
|
||||||
| ErrorResponse String
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- json decoders
|
-- json decoders
|
||||||
|
|
||||||
|
|
||||||
serverResponseDecoder : Decode.Decoder ServerResponse
|
serverResponseDecoder : Decode.Decoder t -> Decode.Decoder (ServerResponse t)
|
||||||
serverResponseDecoder =
|
serverResponseDecoder okDecoder =
|
||||||
Decode.oneOf
|
Decode.oneOf
|
||||||
[ Decode.map OkResponse
|
[ Decode.map OkResponse
|
||||||
(Decode.field "ok" lessonsDecoder)
|
(Decode.field "ok" okDecoder)
|
||||||
, Decode.map ErrorResponse (Decode.field "error" Decode.string)
|
, Decode.map ErrorResponse (Decode.field "error" Decode.string)
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -86,14 +62,43 @@ convertKeysToIntDict stringKeyedDict =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- json encoders
|
||||||
|
|
||||||
|
|
||||||
|
newUserEncoder name creds =
|
||||||
|
Encode.object [ ( "name", Encode.string name ), ( "creds", Encode.string creds ) ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- http command
|
-- http command
|
||||||
|
|
||||||
|
|
||||||
fetchLessons : (Result Http.Error ServerResponse -> msg) -> Cmd msg
|
fetchLessons : Cmd Msg
|
||||||
fetchLessons toMsg =
|
fetchLessons =
|
||||||
Http.get
|
Http.get
|
||||||
{ url = "http://localhost:3000/api/lessons"
|
{ url = "http://localhost:3000/api/lessons"
|
||||||
, expect = Http.expectJson toMsg serverResponseDecoder
|
, expect = Http.expectJson FetchLessons (serverResponseDecoder lessonsDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fetchLesson : Int -> Cmd Msg
|
||||||
|
fetchLesson num =
|
||||||
|
Http.get
|
||||||
|
{ url = "http://localhost:3000/api/lesson/" ++ String.fromInt num
|
||||||
|
, expect = Http.expectJson FetchLesson (serverResponseDecoder lessonDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- post requests
|
||||||
|
|
||||||
|
|
||||||
|
addUser : String -> String -> Cmd Msg
|
||||||
|
addUser name pw =
|
||||||
|
Http.post
|
||||||
|
{ url = "http://localhost:3000/api/lessons"
|
||||||
|
, body = Http.jsonBody (newUserEncoder name pw)
|
||||||
|
, expect = Http.expectJson FetchLessons (serverResponseDecoder lessonsDecoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
109
ui/src/Homepage.elm
Normal file
109
ui/src/Homepage.elm
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
module Homepage exposing (page)
|
||||||
|
|
||||||
|
import Dict
|
||||||
|
import Element exposing (..)
|
||||||
|
import Html exposing (Html)
|
||||||
|
import Types exposing (Lesson, Lessons, Model, Msg(..))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- local state
|
||||||
|
-- type Tab
|
||||||
|
-- = Lessons
|
||||||
|
-- | Words
|
||||||
|
-- | Pronunciation
|
||||||
|
-- tabEl : Tab -> Tab -> Element Msg
|
||||||
|
-- tabEl tab selectedTab =
|
||||||
|
-- let
|
||||||
|
-- isSelected =
|
||||||
|
-- tab == selectedTab
|
||||||
|
-- padOffset =
|
||||||
|
-- if isSelected then
|
||||||
|
-- 0
|
||||||
|
-- else
|
||||||
|
-- 2
|
||||||
|
-- borderWidths =
|
||||||
|
-- if isSelected then
|
||||||
|
-- { left = 2, top = 2, right = 2, bottom = 0 }
|
||||||
|
-- else
|
||||||
|
-- { bottom = 2, top = 0, left = 0, right = 0 }
|
||||||
|
-- corners =
|
||||||
|
-- if isSelected then
|
||||||
|
-- { topLeft = 6, topRight = 6, bottomLeft = 0, bottomRight = 0 }
|
||||||
|
-- else
|
||||||
|
-- { topLeft = 0, topRight = 0, bottomLeft = 0, bottomRight = 0 }
|
||||||
|
-- in
|
||||||
|
-- el
|
||||||
|
-- [ Border.widthEach borderWidths
|
||||||
|
-- , Border.roundEach corners
|
||||||
|
-- , Border.color color.blue
|
||||||
|
-- , onClick <| UserSelectedTab tab
|
||||||
|
-- ]
|
||||||
|
-- <|
|
||||||
|
-- el
|
||||||
|
-- [ centerX
|
||||||
|
-- , centerY
|
||||||
|
-- , paddingEach { left = 30, right = 30, top = 10 + padOffset, bottom = 10 - padOffset }
|
||||||
|
-- ]
|
||||||
|
-- <|
|
||||||
|
-- text <|
|
||||||
|
-- case tab of
|
||||||
|
-- Lessons ->
|
||||||
|
-- "Lessons"
|
||||||
|
-- Words ->
|
||||||
|
-- "Words"
|
||||||
|
-- Pronunciation ->
|
||||||
|
-- "Audio"
|
||||||
|
|
||||||
|
|
||||||
|
page : Model -> Html Msg
|
||||||
|
page model =
|
||||||
|
if model.isLoading then
|
||||||
|
layout [] (text "...")
|
||||||
|
|
||||||
|
else
|
||||||
|
layout [ width fill, height fill ] <|
|
||||||
|
el
|
||||||
|
[ centerX ]
|
||||||
|
(lessonsView model.lessons)
|
||||||
|
|
||||||
|
|
||||||
|
lessonsView : Lessons -> Element Msg
|
||||||
|
lessonsView lessons =
|
||||||
|
Dict.values lessons
|
||||||
|
|> List.map lessonPreview
|
||||||
|
|> column []
|
||||||
|
|
||||||
|
|
||||||
|
lessonPreview : Lesson -> Element Msg
|
||||||
|
lessonPreview lesson =
|
||||||
|
link []
|
||||||
|
{ url = "/lesson/" ++ String.fromInt lesson.id
|
||||||
|
, label =
|
||||||
|
el [] <|
|
||||||
|
column []
|
||||||
|
[ text ("Lesson: " ++ String.fromInt lesson.id)
|
||||||
|
, text lesson.text
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- mainpage : Model -> Html Msg
|
||||||
|
-- mainpage model =
|
||||||
|
-- if model.isLoading then
|
||||||
|
-- layout [] (text "...")
|
||||||
|
-- else
|
||||||
|
-- layout [ width fill, height fill ] <|
|
||||||
|
-- column
|
||||||
|
-- [ centerX ]
|
||||||
|
-- [ row []
|
||||||
|
-- [ tabEl Lessons model.tab
|
||||||
|
-- , tabEl Words model.tab
|
||||||
|
-- , tabEl Pronunciation model.tab
|
||||||
|
-- ]
|
||||||
|
-- , if model.tab == Lessons then
|
||||||
|
-- lessonsView model.lessons
|
||||||
|
-- else
|
||||||
|
-- el [] (text "WIP")
|
||||||
|
-- ]
|
85
ui/src/Lessonpage.elm
Normal file
85
ui/src/Lessonpage.elm
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
module Lessonpage exposing (page)
|
||||||
|
|
||||||
|
import Element exposing (..)
|
||||||
|
import Element.Border as Border
|
||||||
|
import Html exposing (Html)
|
||||||
|
import List.Extra
|
||||||
|
import Types exposing (Card, Lesson, Msg(..))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- local state
|
||||||
|
-- type Tab
|
||||||
|
-- = Lessons
|
||||||
|
-- | Words
|
||||||
|
-- | Pronunciation
|
||||||
|
|
||||||
|
|
||||||
|
page : Lesson -> Int -> Html Msg
|
||||||
|
page lesson cid =
|
||||||
|
let
|
||||||
|
_ =
|
||||||
|
Debug.log "lesson" lesson
|
||||||
|
in
|
||||||
|
layout [ width fill, height fill, inFront navbar ] <|
|
||||||
|
el
|
||||||
|
[ centerX ]
|
||||||
|
(lessonView lesson cid)
|
||||||
|
|
||||||
|
|
||||||
|
lessonView : Lesson -> Int -> Element Msg
|
||||||
|
lessonView lesson cid =
|
||||||
|
case List.Extra.getAt cid lesson.cards of
|
||||||
|
Just c ->
|
||||||
|
cardView c
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
notFound
|
||||||
|
|
||||||
|
|
||||||
|
cardView : Card -> Element Msg
|
||||||
|
cardView card =
|
||||||
|
column []
|
||||||
|
[ el [] (text card.text)
|
||||||
|
, cardNote card.note
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
cardNote : Maybe String -> Element Msg
|
||||||
|
cardNote ms =
|
||||||
|
case ms of
|
||||||
|
Just txt ->
|
||||||
|
el [] (text txt)
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
el [] (text "")
|
||||||
|
|
||||||
|
|
||||||
|
navbar : Element Msg
|
||||||
|
navbar =
|
||||||
|
row
|
||||||
|
[ width fill
|
||||||
|
, padding 20
|
||||||
|
, spacing 20
|
||||||
|
, Border.widthEach { bottom = 2, left = 0, right = 0, top = 0 }
|
||||||
|
]
|
||||||
|
[ el
|
||||||
|
[ width <|
|
||||||
|
px 80
|
||||||
|
, height <|
|
||||||
|
px 40
|
||||||
|
]
|
||||||
|
(text "hi")
|
||||||
|
, el [ alignRight ] <| text "Settings"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- TODO get rid of this, really bad form
|
||||||
|
|
||||||
|
|
||||||
|
notFound : Element Msg
|
||||||
|
notFound =
|
||||||
|
el
|
||||||
|
[ centerX ]
|
||||||
|
(text "404")
|
176
ui/src/Main.elm
176
ui/src/Main.elm
@ -1,49 +1,38 @@
|
|||||||
module Main exposing (..)
|
module Main exposing (..)
|
||||||
|
|
||||||
import Api exposing (Card, Lesson, Lessons, ServerResponse(..), fetchLessons)
|
import Api exposing (fetchLessons)
|
||||||
import Browser
|
import Browser
|
||||||
|
import Browser.Navigation as Nav
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
import Element exposing (..)
|
import Element exposing (..)
|
||||||
import Element.Background as Background
|
import Element.Background as Background
|
||||||
import Element.Border as Border
|
import Element.Border as Border
|
||||||
import Element.Events exposing (onClick)
|
import Element.Events exposing (onClick)
|
||||||
import Element.Font as Font
|
import Element.Font as Font
|
||||||
import Html exposing (Html)
|
import Router exposing (parseUrl, router)
|
||||||
import Http
|
import Types exposing (Lessons, Model, Msg(..), Route(..), ServerResponse(..))
|
||||||
|
import Url
|
||||||
|
|
||||||
type Tab
|
|
||||||
= Lessons
|
|
||||||
| Words
|
|
||||||
| Pronunciation
|
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= UserSelectedTab Tab
|
|
||||||
| FetchDataHandler (Result Http.Error ServerResponse)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- state
|
-- state
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ lessons : Lessons
|
|
||||||
, tab : Tab
|
|
||||||
, isLoading : Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sampleLessons : Lessons
|
sampleLessons : Lessons
|
||||||
sampleLessons =
|
sampleLessons =
|
||||||
Dict.fromList []
|
Dict.fromList []
|
||||||
|
|
||||||
|
|
||||||
initialState : Model
|
initialState : Url.Url -> Nav.Key -> Model
|
||||||
initialState =
|
initialState url key =
|
||||||
|
let
|
||||||
|
route =
|
||||||
|
parseUrl url
|
||||||
|
in
|
||||||
{ isLoading = False
|
{ isLoading = False
|
||||||
, lessons = sampleLessons
|
, lessons = sampleLessons
|
||||||
, tab = Lessons
|
, route = route
|
||||||
|
, key = key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -54,10 +43,7 @@ initialState =
|
|||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
update msg model =
|
update msg model =
|
||||||
case msg of
|
case msg of
|
||||||
UserSelectedTab t ->
|
FetchLessons (Ok serres) ->
|
||||||
( { model | tab = t }, Cmd.none )
|
|
||||||
|
|
||||||
FetchDataHandler (Ok serres) ->
|
|
||||||
-- let
|
-- let
|
||||||
-- _ =
|
-- _ =
|
||||||
-- Debug.log "hi" serres
|
-- Debug.log "hi" serres
|
||||||
@ -69,115 +55,73 @@ update msg model =
|
|||||||
ErrorResponse _ ->
|
ErrorResponse _ ->
|
||||||
( { model | isLoading = False }, Cmd.none )
|
( { model | isLoading = False }, Cmd.none )
|
||||||
|
|
||||||
FetchDataHandler (Err _) ->
|
FetchLessons (Err _) ->
|
||||||
( { model | isLoading = False }, Cmd.none )
|
( { model | isLoading = False }, Cmd.none )
|
||||||
|
|
||||||
|
FetchLesson res ->
|
||||||
|
( { model | isLoading = False }, Cmd.none )
|
||||||
|
|
||||||
view : Model -> Html Msg
|
LinkClicked urlRequest ->
|
||||||
view model =
|
|
||||||
if model.isLoading then
|
|
||||||
layout [] (text "...")
|
|
||||||
|
|
||||||
else
|
|
||||||
layout [ width fill, height fill ] <|
|
|
||||||
column
|
|
||||||
[ centerX ]
|
|
||||||
[ row []
|
|
||||||
[ tabEl Lessons model.tab
|
|
||||||
, tabEl Words model.tab
|
|
||||||
, tabEl Pronunciation model.tab
|
|
||||||
]
|
|
||||||
, if model.tab == Lessons then
|
|
||||||
lessonsView model.lessons
|
|
||||||
|
|
||||||
else
|
|
||||||
el [] (text "WIP")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
tabEl : Tab -> Tab -> Element Msg
|
|
||||||
tabEl tab selectedTab =
|
|
||||||
let
|
let
|
||||||
isSelected =
|
_ =
|
||||||
tab == selectedTab
|
Debug.log "url request" urlRequest
|
||||||
|
|
||||||
padOffset =
|
|
||||||
if isSelected then
|
|
||||||
0
|
|
||||||
|
|
||||||
else
|
|
||||||
2
|
|
||||||
|
|
||||||
borderWidths =
|
|
||||||
if isSelected then
|
|
||||||
{ left = 2, top = 2, right = 2, bottom = 0 }
|
|
||||||
|
|
||||||
else
|
|
||||||
{ bottom = 2, top = 0, left = 0, right = 0 }
|
|
||||||
|
|
||||||
corners =
|
|
||||||
if isSelected then
|
|
||||||
{ topLeft = 6, topRight = 6, bottomLeft = 0, bottomRight = 0 }
|
|
||||||
|
|
||||||
else
|
|
||||||
{ topLeft = 0, topRight = 0, bottomLeft = 0, bottomRight = 0 }
|
|
||||||
in
|
in
|
||||||
el
|
case urlRequest of
|
||||||
[ Border.widthEach borderWidths
|
Browser.Internal url ->
|
||||||
, Border.roundEach corners
|
( model, Nav.pushUrl model.key (Url.toString url) )
|
||||||
, Border.color color.blue
|
|
||||||
, onClick <| UserSelectedTab tab
|
|
||||||
]
|
|
||||||
<|
|
|
||||||
el
|
|
||||||
[ centerX
|
|
||||||
, centerY
|
|
||||||
, paddingEach { left = 30, right = 30, top = 10 + padOffset, bottom = 10 - padOffset }
|
|
||||||
]
|
|
||||||
<|
|
|
||||||
text <|
|
|
||||||
case tab of
|
|
||||||
Lessons ->
|
|
||||||
"Lessons"
|
|
||||||
|
|
||||||
Words ->
|
Browser.External href ->
|
||||||
"Words"
|
( model, Nav.load href )
|
||||||
|
|
||||||
Pronunciation ->
|
UrlChanged url ->
|
||||||
"Audio"
|
let
|
||||||
|
newRoute =
|
||||||
|
parseUrl url
|
||||||
|
in
|
||||||
|
( { model | route = newRoute }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
lessonsView : Lessons -> Element Msg
|
|
||||||
lessonsView lessons =
|
-- SUBSCRIPTIONS
|
||||||
Dict.values lessons
|
|
||||||
|> List.map lessonPreview
|
|
||||||
|> column []
|
|
||||||
|
|
||||||
|
|
||||||
lessonPreview : Lesson -> Element Msg
|
subscriptions : Model -> Sub Msg
|
||||||
lessonPreview lesson =
|
subscriptions _ =
|
||||||
el []
|
Sub.none
|
||||||
(column []
|
|
||||||
[ text ("Lesson: " ++ String.fromInt lesson.id)
|
|
||||||
, text lesson.text
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
init : flags -> ( Model, Cmd Msg )
|
view : Model -> Browser.Document Msg
|
||||||
init flags =
|
view model =
|
||||||
|
let
|
||||||
|
( _, _, html ) =
|
||||||
|
router model
|
||||||
|
|
||||||
|
-- _ =
|
||||||
|
-- Debug.log "model" model
|
||||||
|
in
|
||||||
|
{ title = "Prosody"
|
||||||
|
, body = [ html ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : flags -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
|
||||||
|
init flags url key =
|
||||||
Debug.log "Init flags" flags
|
Debug.log "Init flags" flags
|
||||||
|> (\_ -> Debug.log "Initial State" initialState)
|
-- |> (\_ -> Debug.log "url" url)
|
||||||
|> (\_ -> ( initialState, fetchLessons FetchDataHandler ))
|
-- |> (\_ -> Debug.log "key" key)
|
||||||
|
-- |> (\_ -> Debug.log "Initial State" initialState)
|
||||||
|
|> (\_ -> ( initialState url key, fetchLessons ))
|
||||||
|
|
||||||
|
|
||||||
main : Program () Model Msg
|
main : Program () Model Msg
|
||||||
main =
|
main =
|
||||||
Browser.element
|
Browser.application
|
||||||
{ init = init
|
{ init = init
|
||||||
, view = view
|
, view = view
|
||||||
, update = update
|
, update = update
|
||||||
, subscriptions = \_ -> Sub.none
|
, subscriptions = subscriptions
|
||||||
|
, onUrlRequest = LinkClicked
|
||||||
|
, onUrlChange = UrlChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
61
ui/src/Router.elm
Normal file
61
ui/src/Router.elm
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
module Router exposing (parseUrl, router)
|
||||||
|
|
||||||
|
import Api exposing (fetchLesson)
|
||||||
|
import Dict
|
||||||
|
import Element exposing (..)
|
||||||
|
import Homepage
|
||||||
|
import Html exposing (Html)
|
||||||
|
import Lessonpage
|
||||||
|
import Types exposing (Model, Msg(..), Route(..))
|
||||||
|
import Url
|
||||||
|
import Url.Parser as Parser exposing ((</>), Parser)
|
||||||
|
|
||||||
|
|
||||||
|
routeParser : Parser (Route -> a) a
|
||||||
|
routeParser =
|
||||||
|
Parser.oneOf
|
||||||
|
[ Parser.map Root Parser.top
|
||||||
|
, Parser.map LessonR (Parser.s "lesson" </> Parser.int </> Parser.int)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
parseUrl : Url.Url -> Route
|
||||||
|
parseUrl url =
|
||||||
|
Parser.parse routeParser url |> Maybe.withDefault NotFound
|
||||||
|
|
||||||
|
|
||||||
|
router : Model -> ( Model, Cmd Msg, Html Msg )
|
||||||
|
router model =
|
||||||
|
case model.route of
|
||||||
|
Root ->
|
||||||
|
( model, Cmd.none, Homepage.page model )
|
||||||
|
|
||||||
|
LessonR lid cid ->
|
||||||
|
lessonLoader lid cid model
|
||||||
|
|
||||||
|
NotFound ->
|
||||||
|
( model, Cmd.none, notFound )
|
||||||
|
|
||||||
|
|
||||||
|
lessonLoader : Int -> Int -> Model -> ( Model, Cmd Msg, Html Msg )
|
||||||
|
lessonLoader lid cid model =
|
||||||
|
case Dict.get lid model.lessons of
|
||||||
|
Just lesson ->
|
||||||
|
( model, Cmd.none, Lessonpage.page lesson cid )
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
( { model | isLoading = True }, fetchLesson lid, loadingPage )
|
||||||
|
|
||||||
|
|
||||||
|
notFound : Html Msg
|
||||||
|
notFound =
|
||||||
|
layout [] <|
|
||||||
|
el
|
||||||
|
[ centerX ]
|
||||||
|
(text "404")
|
||||||
|
|
||||||
|
|
||||||
|
loadingPage : Html Msg
|
||||||
|
loadingPage =
|
||||||
|
layout [] <|
|
||||||
|
el [ centerX, padding 20 ] (text "Loading lesson...")
|
53
ui/src/Types.elm
Normal file
53
ui/src/Types.elm
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
module Types exposing (..)
|
||||||
|
|
||||||
|
import Browser
|
||||||
|
import Browser.Navigation as Nav
|
||||||
|
import Dict exposing (Dict)
|
||||||
|
import Http
|
||||||
|
import Url
|
||||||
|
|
||||||
|
|
||||||
|
type Route
|
||||||
|
= Root
|
||||||
|
| LessonR Int Int
|
||||||
|
| NotFound
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ lessons : Lessons
|
||||||
|
|
||||||
|
-- , tab : Tab
|
||||||
|
, isLoading : Bool
|
||||||
|
, key : Nav.Key
|
||||||
|
, route : Route
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type ServerResponse t
|
||||||
|
= OkResponse t
|
||||||
|
| ErrorResponse String
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= FetchLessons (Result Http.Error (ServerResponse Lessons))
|
||||||
|
| FetchLesson (Result Http.Error (ServerResponse Lesson))
|
||||||
|
| LinkClicked Browser.UrlRequest
|
||||||
|
| UrlChanged Url.Url
|
||||||
|
|
||||||
|
|
||||||
|
type alias Card =
|
||||||
|
{ text : String
|
||||||
|
, note : Maybe String
|
||||||
|
, id : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Lesson =
|
||||||
|
{ text : String
|
||||||
|
, id : Int
|
||||||
|
, cards : List Card
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Lessons =
|
||||||
|
Dict Int Lesson
|
Loading…
Reference in New Issue
Block a user