mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
ui: Get UI to compile with Elm 0.19
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"summary": "Elm powered UI for Inbucket",
|
||||
"repository": "https://github.com/jhillyerd/inbucket.git",
|
||||
"license": "MIT",
|
||||
"source-directories": [
|
||||
"src"
|
||||
],
|
||||
"exposed-modules": [],
|
||||
"proxy": {
|
||||
"/api": {
|
||||
"target": "http://localhost:9000",
|
||||
"ws": true
|
||||
},
|
||||
"/debug": {
|
||||
"target": "http://localhost:9000"
|
||||
},
|
||||
"/serve": {
|
||||
"target": "http://localhost:9000"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0",
|
||||
"basti1302/elm-human-readable-filesize": "1.1.0 <= v < 2.0.0",
|
||||
"elm-lang/core": "5.1.1 <= v < 6.0.0",
|
||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||
"elm-lang/http": "1.0.0 <= v < 2.0.0",
|
||||
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
|
||||
"elm-lang/svg": "2.0.0 <= v < 3.0.0",
|
||||
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
|
||||
"jweir/sparkline": "3.0.0 <= v < 4.0.0",
|
||||
"ryannhg/elm-date-format": "2.1.2 <= v < 3.0.0"
|
||||
},
|
||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
||||
}
|
||||
32
ui/elm.json
Normal file
32
ui/elm.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "application",
|
||||
"source-directories": [
|
||||
"src"
|
||||
],
|
||||
"elm-version": "0.19.0",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
|
||||
"basti1302/elm-human-readable-filesize": "1.1.1",
|
||||
"elm/browser": "1.0.1",
|
||||
"elm/core": "1.0.0",
|
||||
"elm/html": "1.0.0",
|
||||
"elm/http": "1.0.0",
|
||||
"elm/json": "1.0.0",
|
||||
"elm/svg": "1.0.1",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"jweir/sparkline": "4.0.0",
|
||||
"ryannhg/date-format": "2.1.0"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/regex": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.2",
|
||||
"myrho/elm-round": "1.0.4"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
"direct": {},
|
||||
"indirect": {}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,11 @@
|
||||
module Data.Date exposing (date)
|
||||
|
||||
import Date exposing (Date)
|
||||
import Json.Decode as Decode exposing (..)
|
||||
import Time exposing (Posix)
|
||||
|
||||
|
||||
{-| Decode an ISO 8601 date
|
||||
{-| Decode a POSIX milliseconds timestamp. Currently faked until backend API is updated.
|
||||
-}
|
||||
date : Decoder Date
|
||||
date : Decoder Posix
|
||||
date =
|
||||
let
|
||||
convert : String -> Decoder Date
|
||||
convert raw =
|
||||
case Date.fromString raw of
|
||||
Ok date ->
|
||||
succeed date
|
||||
|
||||
Err error ->
|
||||
fail error
|
||||
in
|
||||
string |> andThen convert
|
||||
succeed (Time.millisToPosix 0)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module Data.Message exposing (Attachment, Message, attachmentDecoder, decoder)
|
||||
|
||||
import Data.Date exposing (date)
|
||||
import Date exposing (Date)
|
||||
import Json.Decode as Decode exposing (..)
|
||||
import Json.Decode.Pipeline exposing (..)
|
||||
import Time exposing (Posix)
|
||||
|
||||
|
||||
type alias Message =
|
||||
@@ -12,7 +12,7 @@ type alias Message =
|
||||
, from : String
|
||||
, to : List String
|
||||
, subject : String
|
||||
, date : Date
|
||||
, date : Posix
|
||||
, size : Int
|
||||
, seen : Bool
|
||||
, text : String
|
||||
@@ -30,7 +30,7 @@ type alias Attachment =
|
||||
|
||||
decoder : Decoder Message
|
||||
decoder =
|
||||
decode Message
|
||||
succeed Message
|
||||
|> required "mailbox" string
|
||||
|> required "id" string
|
||||
|> optional "from" string ""
|
||||
@@ -46,7 +46,7 @@ decoder =
|
||||
|
||||
attachmentDecoder : Decoder Attachment
|
||||
attachmentDecoder =
|
||||
decode Attachment
|
||||
succeed Attachment
|
||||
|> required "id" string
|
||||
|> required "filename" string
|
||||
|> required "content-type" string
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module Data.MessageHeader exposing (MessageHeader, decoder)
|
||||
|
||||
import Data.Date exposing (date)
|
||||
import Date exposing (Date)
|
||||
import Json.Decode as Decode exposing (..)
|
||||
import Json.Decode.Pipeline exposing (..)
|
||||
import Time exposing (Posix)
|
||||
|
||||
|
||||
type alias MessageHeader =
|
||||
@@ -12,7 +12,7 @@ type alias MessageHeader =
|
||||
, from : String
|
||||
, to : List String
|
||||
, subject : String
|
||||
, date : Date
|
||||
, date : Posix
|
||||
, size : Int
|
||||
, seen : Bool
|
||||
}
|
||||
@@ -20,7 +20,7 @@ type alias MessageHeader =
|
||||
|
||||
decoder : Decoder MessageHeader
|
||||
decoder =
|
||||
decode MessageHeader
|
||||
succeed MessageHeader
|
||||
|> required "mailbox" string
|
||||
|> required "id" string
|
||||
|> optional "from" string ""
|
||||
|
||||
@@ -31,7 +31,7 @@ type alias Metrics =
|
||||
|
||||
decoder : Decoder Metrics
|
||||
decoder =
|
||||
decode Metrics
|
||||
succeed Metrics
|
||||
|> requiredAt [ "memstats", "Sys" ] int
|
||||
|> requiredAt [ "memstats", "HeapSys" ] int
|
||||
|> requiredAt [ "memstats", "HeapAlloc" ] int
|
||||
@@ -59,4 +59,4 @@ decoder =
|
||||
-}
|
||||
decodeIntList : Decoder (List Int)
|
||||
decodeIntList =
|
||||
map (String.split "," >> List.map (String.toInt >> Result.withDefault 0)) string
|
||||
map (String.split "," >> List.map (String.toInt >> Maybe.withDefault 0)) string
|
||||
|
||||
@@ -9,13 +9,15 @@ module Data.Session exposing
|
||||
, update
|
||||
)
|
||||
|
||||
import Json.Decode as Decode exposing (..)
|
||||
import Browser.Navigation as Nav
|
||||
import Json.Decode exposing (..)
|
||||
import Json.Decode.Pipeline exposing (..)
|
||||
import Navigation exposing (Location)
|
||||
import Url exposing (Url)
|
||||
|
||||
|
||||
type alias Session =
|
||||
{ host : String
|
||||
{ key : Nav.Key
|
||||
, host : String
|
||||
, flash : String
|
||||
, routing : Bool
|
||||
, persistent : Persistent
|
||||
@@ -36,9 +38,9 @@ type Msg
|
||||
| AddRecent String
|
||||
|
||||
|
||||
init : Location -> Persistent -> Session
|
||||
init location persistent =
|
||||
Session location.host "" True persistent
|
||||
init : Nav.Key -> Url -> Persistent -> Session
|
||||
init key location persistent =
|
||||
Session key location.host "" True persistent
|
||||
|
||||
|
||||
update : Msg -> Session -> Session
|
||||
@@ -84,10 +86,10 @@ none =
|
||||
|
||||
decoder : Decoder Persistent
|
||||
decoder =
|
||||
decode Persistent
|
||||
succeed Persistent
|
||||
|> optional "recentMailboxes" (list string) []
|
||||
|
||||
|
||||
decodeValueWithDefault : Value -> Persistent
|
||||
decodeValueWithDefault =
|
||||
Decode.decodeValue decoder >> Result.withDefault { recentMailboxes = [] }
|
||||
decodeValue decoder >> Result.withDefault { recentMailboxes = [] }
|
||||
|
||||
@@ -42,11 +42,11 @@ errorString error =
|
||||
"HTTP Network error"
|
||||
|
||||
Http.BadStatus res ->
|
||||
"Bad HTTP status: " ++ toString res.status.code
|
||||
"Bad HTTP status: " ++ String.fromInt res.status.code
|
||||
|
||||
Http.BadPayload msg res ->
|
||||
"Bad HTTP payload: "
|
||||
++ msg
|
||||
++ " ("
|
||||
++ toString res.status.code
|
||||
++ String.fromInt res.status.code
|
||||
++ ")"
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
module Main exposing (Model, Msg(..), Page(..), applySession, init, main, pageSubscriptions, sessionChange, setRoute, subscriptions, update, updatePage, view)
|
||||
module Main exposing
|
||||
( Model
|
||||
, Msg(..)
|
||||
, Page(..)
|
||||
, applySession
|
||||
, init
|
||||
, main
|
||||
, pageSubscriptions
|
||||
, sessionChange
|
||||
, setRoute
|
||||
, subscriptions
|
||||
, update
|
||||
, updatePage
|
||||
, view
|
||||
)
|
||||
|
||||
import Browser exposing (Document, UrlRequest)
|
||||
import Browser.Navigation as Nav
|
||||
import Data.Session as Session exposing (Session, decoder)
|
||||
import Html exposing (..)
|
||||
import Json.Decode as Decode exposing (Value)
|
||||
import Navigation exposing (Location)
|
||||
import Json.Decode as D exposing (Value)
|
||||
import Page.Home as Home
|
||||
import Page.Mailbox as Mailbox
|
||||
import Page.Monitor as Monitor
|
||||
import Page.Status as Status
|
||||
import Ports
|
||||
import Route exposing (Route)
|
||||
import Url exposing (Url)
|
||||
import Views.Page as Page exposing (ActivePage(..), frame)
|
||||
|
||||
|
||||
@@ -31,11 +47,11 @@ type alias Model =
|
||||
}
|
||||
|
||||
|
||||
init : Value -> Location -> ( Model, Cmd Msg )
|
||||
init sessionValue location =
|
||||
init : Value -> Url -> Nav.Key -> ( Model, Cmd Msg )
|
||||
init sessionValue location key =
|
||||
let
|
||||
session =
|
||||
Session.init location (Session.decodeValueWithDefault sessionValue)
|
||||
Session.init key location (Session.decodeValueWithDefault sessionValue)
|
||||
|
||||
( subModel, _ ) =
|
||||
Home.init
|
||||
@@ -47,15 +63,16 @@ init sessionValue location =
|
||||
}
|
||||
|
||||
route =
|
||||
Route.fromLocation location
|
||||
Route.fromUrl location
|
||||
in
|
||||
applySession (setRoute route model)
|
||||
|
||||
|
||||
type Msg
|
||||
= SetRoute Route
|
||||
| NewRoute Route
|
||||
| UpdateSession (Result String Session.Persistent)
|
||||
| UrlChanged Url
|
||||
| LinkClicked UrlRequest
|
||||
| UpdateSession (Result D.Error Session.Persistent)
|
||||
| MailboxNameInput String
|
||||
| ViewMailbox String
|
||||
| HomeMsg Home.Msg
|
||||
@@ -76,9 +93,9 @@ subscriptions model =
|
||||
]
|
||||
|
||||
|
||||
sessionChange : Sub (Result String Session.Persistent)
|
||||
sessionChange : Sub (Result D.Error Session.Persistent)
|
||||
sessionChange =
|
||||
Ports.onSessionChange (Decode.decodeValue Session.decoder)
|
||||
Ports.onSessionChange (D.decodeValue Session.decoder)
|
||||
|
||||
|
||||
pageSubscriptions : Page -> Sub Msg
|
||||
@@ -105,19 +122,27 @@ update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
applySession <|
|
||||
case msg of
|
||||
SetRoute route ->
|
||||
-- Updates broser URL to requested route.
|
||||
( model, Route.newUrl route, Session.none )
|
||||
LinkClicked req ->
|
||||
case req of
|
||||
Browser.Internal url ->
|
||||
( model, Nav.pushUrl model.session.key (Url.toString url), Session.none )
|
||||
|
||||
NewRoute route ->
|
||||
_ ->
|
||||
Debug.todo "implement external links"
|
||||
|
||||
UrlChanged url ->
|
||||
-- Responds to new browser URL.
|
||||
if model.session.routing then
|
||||
setRoute route model
|
||||
setRoute (Route.fromUrl url) model
|
||||
|
||||
else
|
||||
-- Skip once, but re-enable routing.
|
||||
( model, Cmd.none, Session.EnableRouting )
|
||||
|
||||
SetRoute route ->
|
||||
-- Updates broser URL to requested route.
|
||||
( model, Route.newUrl model.session.key route, Session.none )
|
||||
|
||||
UpdateSession (Ok persistent) ->
|
||||
let
|
||||
session =
|
||||
@@ -140,7 +165,7 @@ update msg model =
|
||||
|
||||
ViewMailbox name ->
|
||||
( { model | mailboxName = "" }
|
||||
, Route.newUrl (Route.Mailbox name)
|
||||
, Route.newUrl model.session.key (Route.Mailbox name)
|
||||
, Session.none
|
||||
)
|
||||
|
||||
@@ -269,7 +294,7 @@ applySession ( model, cmd, sessionMsg ) =
|
||||
-- VIEW
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view : Model -> Document Msg
|
||||
view model =
|
||||
let
|
||||
mailbox =
|
||||
@@ -291,7 +316,8 @@ view model =
|
||||
frame =
|
||||
Page.frame controls model.session
|
||||
in
|
||||
case model.page of
|
||||
Document "Inbucket Document"
|
||||
[ case model.page of
|
||||
Home subModel ->
|
||||
Html.map HomeMsg (Home.view model.session subModel)
|
||||
|> frame Page.Other
|
||||
@@ -307,6 +333,7 @@ view model =
|
||||
Status subModel ->
|
||||
Html.map StatusMsg (Status.view model.session subModel)
|
||||
|> frame Page.Status
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -315,9 +342,11 @@ view model =
|
||||
|
||||
main : Program Value Model Msg
|
||||
main =
|
||||
Navigation.programWithFlags (Route.fromLocation >> NewRoute)
|
||||
Browser.application
|
||||
{ init = init
|
||||
, view = view
|
||||
, update = update
|
||||
, subscriptions = subscriptions
|
||||
, onUrlChange = UrlChanged
|
||||
, onUrlRequest = LinkClicked
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ module Page.Mailbox exposing (Model, Msg, init, load, subscriptions, update, vie
|
||||
import Data.Message as Message exposing (Message)
|
||||
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Date exposing (Date)
|
||||
import DateFormat
|
||||
import DateFormat.Relative as Relative
|
||||
import Html exposing (..)
|
||||
@@ -11,7 +10,7 @@ import Html.Attributes
|
||||
exposing
|
||||
( class
|
||||
, classList
|
||||
, downloadAs
|
||||
, download
|
||||
, href
|
||||
, id
|
||||
, placeholder
|
||||
@@ -28,7 +27,7 @@ import Json.Encode as Encode
|
||||
import Ports
|
||||
import Route
|
||||
import Task
|
||||
import Time exposing (Time)
|
||||
import Time exposing (Posix)
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +64,7 @@ type alias MessageList =
|
||||
|
||||
type alias VisibleMessage =
|
||||
{ message : Message
|
||||
, markSeenAt : Maybe Time
|
||||
, markSeenAt : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
@@ -74,13 +73,13 @@ type alias Model =
|
||||
, state : State
|
||||
, bodyMode : Body
|
||||
, searchInput : String
|
||||
, now : Date
|
||||
, now : Posix
|
||||
}
|
||||
|
||||
|
||||
init : String -> Maybe MessageID -> ( Model, Cmd Msg )
|
||||
init mailboxName selection =
|
||||
( Model mailboxName (LoadingList selection) SafeHtmlBody "" (Date.fromTime 0)
|
||||
( Model mailboxName (LoadingList selection) SafeHtmlBody "" (Time.millisToPosix 0)
|
||||
, load mailboxName
|
||||
)
|
||||
|
||||
@@ -108,13 +107,13 @@ subscriptions model =
|
||||
Sub.none
|
||||
|
||||
else
|
||||
Time.every (250 * Time.millisecond) SeenTick
|
||||
Time.every 250 SeenTick
|
||||
|
||||
_ ->
|
||||
Sub.none
|
||||
in
|
||||
Sub.batch
|
||||
[ Time.every (30 * Time.second) Tick
|
||||
[ Time.every (30 * 1000) Tick
|
||||
, subSeen
|
||||
]
|
||||
|
||||
@@ -131,12 +130,12 @@ type Msg
|
||||
| MarkSeenResult (Result Http.Error ())
|
||||
| MessageResult (Result Http.Error Message)
|
||||
| MessageBody Body
|
||||
| OpenedTime Time
|
||||
| OpenedTime Posix
|
||||
| Purge
|
||||
| PurgeResult (Result Http.Error ())
|
||||
| SearchInput String
|
||||
| SeenTick Time
|
||||
| Tick Time
|
||||
| SeenTick Posix
|
||||
| Tick Posix
|
||||
| ViewMessage MessageID
|
||||
|
||||
|
||||
@@ -147,7 +146,7 @@ update session msg model =
|
||||
( updateSelected model id
|
||||
, Cmd.batch
|
||||
[ -- Update browser location.
|
||||
Route.newUrl (Route.Message model.mailboxName id)
|
||||
Route.newUrl session.key (Route.Message model.mailboxName id)
|
||||
, getMessage model.mailboxName id
|
||||
]
|
||||
, Session.DisableRouting
|
||||
@@ -216,13 +215,17 @@ update session msg model =
|
||||
( model, Cmd.none, Session.none )
|
||||
|
||||
else
|
||||
-- Set delay before reporting message as seen to backend.
|
||||
-- Set 1500ms delay before reporting message as seen to backend.
|
||||
let
|
||||
markSeenAt =
|
||||
Time.posixToMillis time + 1500
|
||||
in
|
||||
( { model
|
||||
| state =
|
||||
ShowingList list
|
||||
(ShowingMessage
|
||||
{ visible
|
||||
| markSeenAt = Just (time + (1.5 * Time.second))
|
||||
| markSeenAt = Just markSeenAt
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -247,7 +250,7 @@ update session msg model =
|
||||
ShowingList _ (ShowingMessage { message, markSeenAt }) ->
|
||||
case markSeenAt of
|
||||
Just deadline ->
|
||||
if now >= deadline then
|
||||
if Time.posixToMillis now >= deadline then
|
||||
updateMarkMessageSeen model message
|
||||
|
||||
else
|
||||
@@ -260,7 +263,7 @@ update session msg model =
|
||||
( model, Cmd.none, Session.none )
|
||||
|
||||
Tick now ->
|
||||
( { model | now = Date.fromTime now }, Cmd.none, Session.none )
|
||||
( { model | now = now }, Cmd.none, Session.none )
|
||||
|
||||
|
||||
{-| Replace the currently displayed message.
|
||||
@@ -530,14 +533,14 @@ messageChip model selected message =
|
||||
viewMessage : Message -> Body -> Html Msg
|
||||
viewMessage message bodyMode =
|
||||
let
|
||||
sourceUrl message =
|
||||
sourceUrl =
|
||||
"/serve/m/" ++ message.mailbox ++ "/" ++ message.id ++ "/source"
|
||||
in
|
||||
div []
|
||||
[ div [ class "button-bar" ]
|
||||
[ button [ class "danger", onClick (DeleteMessage message) ] [ text "Delete" ]
|
||||
, a
|
||||
[ href (sourceUrl message), target "_blank" ]
|
||||
[ href sourceUrl, target "_blank" ]
|
||||
[ button [] [ text "Source" ] ]
|
||||
]
|
||||
, dl [ id "message-header" ]
|
||||
@@ -616,16 +619,16 @@ attachmentRow baseUrl attach =
|
||||
[ a [ href url, target "_blank" ] [ text attach.fileName ]
|
||||
, text (" (" ++ attach.contentType ++ ") ")
|
||||
]
|
||||
, td [] [ a [ href url, downloadAs attach.fileName, class "button" ] [ text "Download" ] ]
|
||||
, td [] [ a [ href url, download attach.fileName, class "button" ] [ text "Download" ] ]
|
||||
]
|
||||
|
||||
|
||||
relativeDate : Model -> Date -> Html Msg
|
||||
relativeDate : Model -> Posix -> Html Msg
|
||||
relativeDate model date =
|
||||
Relative.relativeTime model.now date |> text
|
||||
|
||||
|
||||
verboseDate : Date -> Html Msg
|
||||
verboseDate : Posix -> Html Msg
|
||||
verboseDate date =
|
||||
DateFormat.format
|
||||
[ DateFormat.monthNameFull
|
||||
@@ -642,6 +645,7 @@ verboseDate date =
|
||||
, DateFormat.text " "
|
||||
, DateFormat.amPmUppercase
|
||||
]
|
||||
Time.utc
|
||||
date
|
||||
|> text
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ module Page.Monitor exposing (Model, Msg, init, subscriptions, update, view)
|
||||
|
||||
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Date exposing (Date)
|
||||
import DateFormat
|
||||
exposing
|
||||
( amPmUppercase
|
||||
@@ -10,7 +9,7 @@ import DateFormat
|
||||
, format
|
||||
, hourNumber
|
||||
, minuteFixed
|
||||
, monthNameFirstThree
|
||||
, monthNameAbbreviated
|
||||
)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
@@ -18,6 +17,7 @@ import Html.Events as Events
|
||||
import Json.Decode as D
|
||||
import Ports
|
||||
import Route
|
||||
import Time exposing (Posix)
|
||||
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ subscriptions model =
|
||||
|
||||
|
||||
type Msg
|
||||
= MonitorResult (Result String MonitorMessage)
|
||||
= MonitorResult (Result D.Error MonitorMessage)
|
||||
| OpenMessage MessageHeader
|
||||
|
||||
|
||||
@@ -78,15 +78,15 @@ update session msg model =
|
||||
MonitorResult (Ok (Connected status)) ->
|
||||
( { model | connected = status }, Cmd.none, Session.none )
|
||||
|
||||
MonitorResult (Ok (Message msg)) ->
|
||||
( { model | messages = msg :: model.messages }, Cmd.none, Session.none )
|
||||
MonitorResult (Ok (Message header)) ->
|
||||
( { model | messages = header :: model.messages }, Cmd.none, Session.none )
|
||||
|
||||
MonitorResult (Err err) ->
|
||||
( model, Cmd.none, Session.SetFlash err )
|
||||
( model, Cmd.none, Session.SetFlash (D.errorToString err) )
|
||||
|
||||
OpenMessage msg ->
|
||||
OpenMessage header ->
|
||||
( model
|
||||
, Route.newUrl (Route.Message msg.mailbox msg.id)
|
||||
, Route.newUrl session.key (Route.Message header.mailbox header.id)
|
||||
, Session.none
|
||||
)
|
||||
|
||||
@@ -133,12 +133,12 @@ viewMessage message =
|
||||
]
|
||||
|
||||
|
||||
shortDate : Date -> Html Msg
|
||||
shortDate : Posix -> Html Msg
|
||||
shortDate date =
|
||||
format
|
||||
[ dayOfMonthFixed
|
||||
, DateFormat.text "-"
|
||||
, monthNameFirstThree
|
||||
, monthNameAbbreviated
|
||||
, DateFormat.text " "
|
||||
, hourNumber
|
||||
, DateFormat.text ":"
|
||||
@@ -146,5 +146,6 @@ shortDate date =
|
||||
, DateFormat.text " "
|
||||
, amPmUppercase
|
||||
]
|
||||
Time.utc
|
||||
date
|
||||
|> text
|
||||
|
||||
@@ -9,7 +9,7 @@ import Http exposing (Error)
|
||||
import HttpUtil
|
||||
import Sparkline exposing (DataSet, Point, Size, sparkline)
|
||||
import Svg.Attributes as SvgAttrib
|
||||
import Time exposing (Time)
|
||||
import Time exposing (Posix)
|
||||
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ load =
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Time.every (10 * Time.second) Tick
|
||||
Time.every (10 * 1000) Tick
|
||||
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ subscriptions model =
|
||||
|
||||
type Msg
|
||||
= NewMetrics (Result Http.Error Metrics)
|
||||
| Tick Time
|
||||
| Tick Posix
|
||||
|
||||
|
||||
update : Session -> Msg -> Model -> ( Model, Cmd Msg, Session.Msg )
|
||||
@@ -256,7 +256,7 @@ viewMetric metric =
|
||||
, div [ class "value" ] [ text (metric.formatter metric.value) ]
|
||||
, div [ class "graph" ]
|
||||
[ metric.graph metric.history
|
||||
, text ("(" ++ toString metric.minutes ++ "min)")
|
||||
, text ("(" ++ String.fromInt metric.minutes ++ "min)")
|
||||
]
|
||||
]
|
||||
|
||||
@@ -280,7 +280,11 @@ graphNull =
|
||||
|
||||
graphSize : Size
|
||||
graphSize =
|
||||
( 180, 16, 0, 0 )
|
||||
{ width = 180
|
||||
, height = 16
|
||||
, marginLR = 0
|
||||
, marginTB = 0
|
||||
}
|
||||
|
||||
|
||||
areaStyle : Sparkline.Param a -> Sparkline.Param a
|
||||
@@ -400,4 +404,4 @@ fmtInt n =
|
||||
else
|
||||
thousands (String.slice 0 -3 str) ++ "," ++ String.right 3 str
|
||||
in
|
||||
thousands (toString n)
|
||||
thousands (String.fromInt n)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
module Route exposing (Route(..), fromLocation, href, modifyUrl, newUrl)
|
||||
module Route exposing (Route(..), fromUrl, href, modifyUrl, newUrl)
|
||||
|
||||
import Browser.Navigation as Navigation exposing (Key)
|
||||
import Html exposing (Attribute)
|
||||
import Html.Attributes as Attr
|
||||
import Navigation exposing (Location)
|
||||
import UrlParser as Url exposing ((</>), Parser, parseHash, s, string)
|
||||
import Url exposing (Url)
|
||||
import Url.Parser as Parser exposing ((</>), Parser, map, oneOf, s, string, top)
|
||||
|
||||
|
||||
type Route
|
||||
@@ -15,14 +16,14 @@ type Route
|
||||
| Status
|
||||
|
||||
|
||||
matcher : Parser (Route -> a) a
|
||||
matcher =
|
||||
Url.oneOf
|
||||
[ Url.map Home (s "")
|
||||
, Url.map Message (s "m" </> string </> string)
|
||||
, Url.map Mailbox (s "m" </> string)
|
||||
, Url.map Monitor (s "monitor")
|
||||
, Url.map Status (s "status")
|
||||
routeParser : Parser (Route -> a) a
|
||||
routeParser =
|
||||
oneOf
|
||||
[ map Home top
|
||||
, map Message (s "m" </> string </> string)
|
||||
, map Mailbox (s "m" </> string)
|
||||
, map Monitor (s "monitor")
|
||||
, map Status (s "status")
|
||||
]
|
||||
|
||||
|
||||
@@ -49,37 +50,35 @@ routeToString page =
|
||||
Status ->
|
||||
[ "status" ]
|
||||
in
|
||||
"/#/" ++ String.join "/" pieces
|
||||
"/" ++ String.join "/" pieces
|
||||
|
||||
|
||||
|
||||
-- PUBLIC HELPERS
|
||||
|
||||
|
||||
href : Route -> Attribute msg
|
||||
href route =
|
||||
href : Key -> Route -> Attribute msg
|
||||
href key route =
|
||||
Attr.href (routeToString route)
|
||||
|
||||
|
||||
modifyUrl : Route -> Cmd msg
|
||||
modifyUrl =
|
||||
routeToString >> Navigation.modifyUrl
|
||||
modifyUrl : Key -> Route -> Cmd msg
|
||||
modifyUrl key =
|
||||
routeToString >> Navigation.replaceUrl key
|
||||
|
||||
|
||||
newUrl : Route -> Cmd msg
|
||||
newUrl =
|
||||
routeToString >> Navigation.newUrl
|
||||
newUrl : Key -> Route -> Cmd msg
|
||||
newUrl key =
|
||||
routeToString >> Navigation.pushUrl key
|
||||
|
||||
|
||||
fromLocation : Location -> Route
|
||||
fromLocation location =
|
||||
if String.isEmpty location.hash then
|
||||
Home
|
||||
|
||||
else
|
||||
case parseHash matcher location of
|
||||
{-| Returns the Route for a given URL; by matching the path after # (fragment.)
|
||||
-}
|
||||
fromUrl : Url -> Route
|
||||
fromUrl location =
|
||||
case Parser.parse routeParser location of
|
||||
Nothing ->
|
||||
Unknown location.hash
|
||||
Unknown location.path
|
||||
|
||||
Just route ->
|
||||
route
|
||||
|
||||
@@ -39,10 +39,11 @@ frame controls session page content =
|
||||
div [ id "app" ]
|
||||
[ header []
|
||||
[ ul [ class "navbar", attribute "role" "navigation" ]
|
||||
[ li [ id "navbar-brand" ] [ a [ Route.href Route.Home ] [ text "@ inbucket" ] ]
|
||||
, navbarLink page Route.Monitor [ text "Monitor" ]
|
||||
, navbarLink page Route.Status [ text "Status" ]
|
||||
, navbarRecent page controls
|
||||
[ li [ id "navbar-brand" ]
|
||||
[ a [ Route.href session.key Route.Home ] [ text "@ inbucket" ] ]
|
||||
, navbarLink session page Route.Monitor [ text "Monitor" ]
|
||||
, navbarLink session page Route.Status [ text "Status" ]
|
||||
, navbarRecent session page controls
|
||||
, li [ id "navbar-mailbox" ]
|
||||
[ form [ Events.onSubmit (controls.viewMailbox controls.mailboxValue) ]
|
||||
[ input
|
||||
@@ -70,19 +71,19 @@ frame controls session page content =
|
||||
]
|
||||
|
||||
|
||||
navbarLink : ActivePage -> Route -> List (Html a) -> Html a
|
||||
navbarLink page route linkContent =
|
||||
navbarLink : Session -> ActivePage -> Route -> List (Html a) -> Html a
|
||||
navbarLink session page route linkContent =
|
||||
li [ classList [ ( "navbar-active", isActive page route ) ] ]
|
||||
[ a [ Route.href route ] linkContent ]
|
||||
[ a [ Route.href session.key route ] linkContent ]
|
||||
|
||||
|
||||
{-| Renders list of recent mailboxes, selecting the currently active mailbox.
|
||||
-}
|
||||
navbarRecent : ActivePage -> FrameControls msg -> Html msg
|
||||
navbarRecent page controls =
|
||||
navbarRecent : Session -> ActivePage -> FrameControls msg -> Html msg
|
||||
navbarRecent session page controls =
|
||||
let
|
||||
recentItemLink mailbox =
|
||||
a [ Route.href (Route.Mailbox mailbox) ] [ text mailbox ]
|
||||
a [ Route.href session.key (Route.Mailbox mailbox) ] [ text mailbox ]
|
||||
|
||||
active =
|
||||
page == Mailbox
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import './main.css'
|
||||
import { Main } from './Main.elm'
|
||||
import { Elm } from './Main.elm'
|
||||
import registerServiceWorker from './registerServiceWorker'
|
||||
import registerMonitorPorts from './registerMonitor'
|
||||
|
||||
// App startup.
|
||||
var app = Main.embed(document.getElementById('root'), sessionObject())
|
||||
var app = Elm.Main.init({
|
||||
node: document.getElementById('root'),
|
||||
flags: sessionObject()
|
||||
})
|
||||
|
||||
// Message monitor.
|
||||
registerMonitorPorts(app)
|
||||
|
||||
Reference in New Issue
Block a user