From d05eb1085186e7fbda7dd449fcca5fa24d9cd7e6 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Tue, 6 Nov 2018 20:47:15 -0800 Subject: [PATCH] ui: Friendly date format for mailbox list, monitor --- ui/elm-package.json | 3 +- ui/src/Data/MessageHeader.elm | 20 ++++++++++++-- ui/src/Main.elm | 24 ++++++++++------ ui/src/Page/Mailbox.elm | 52 +++++++++++++++++++++++++---------- ui/src/Page/Monitor.elm | 34 +++++++++++++++++++++-- 5 files changed, 104 insertions(+), 29 deletions(-) diff --git a/ui/elm-package.json b/ui/elm-package.json index 95bd2ee..0ce064d 100644 --- a/ui/elm-package.json +++ b/ui/elm-package.json @@ -29,7 +29,8 @@ "elm-lang/svg": "2.0.0 <= v < 3.0.0", "elm-lang/websocket": "1.0.2 <= v < 2.0.0", "evancz/url-parser": "2.0.1 <= v < 3.0.0", - "jweir/sparkline": "3.0.0 <= v < 4.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" } diff --git a/ui/src/Data/MessageHeader.elm b/ui/src/Data/MessageHeader.elm index 8008058..d3a7e06 100644 --- a/ui/src/Data/MessageHeader.elm +++ b/ui/src/Data/MessageHeader.elm @@ -1,5 +1,6 @@ module Data.MessageHeader exposing (..) +import Date exposing (Date) import Json.Decode as Decode exposing (..) import Json.Decode.Pipeline exposing (..) @@ -10,7 +11,7 @@ type alias MessageHeader = , from : String , to : List String , subject : String - , date : String + , date : Date , size : Int , seen : Bool } @@ -24,6 +25,21 @@ decoder = |> optional "from" string "" |> required "to" (list string) |> optional "subject" string "" - |> required "date" string + |> required "date" date |> required "size" int |> required "seen" bool + + +date : Decoder Date +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 diff --git a/ui/src/Main.elm b/ui/src/Main.elm index 8563fc2..77ec0fc 100644 --- a/ui/src/Main.elm +++ b/ui/src/Main.elm @@ -188,16 +188,24 @@ setRoute route model = ) Route.Mailbox name -> - ( { model | page = Mailbox (Mailbox.init name Nothing) } - , Cmd.map MailboxMsg (Mailbox.load name) - , Session.none - ) + let + ( subModel, subCmd ) = + Mailbox.init name Nothing + in + ( { model | page = Mailbox subModel } + , Cmd.map MailboxMsg subCmd + , Session.none + ) Route.Message mailbox id -> - ( { model | page = Mailbox (Mailbox.init mailbox (Just id)) } - , Cmd.map MailboxMsg (Mailbox.load mailbox) - , Session.none - ) + let + ( subModel, subCmd ) = + Mailbox.init mailbox (Just id) + in + ( { model | page = Mailbox subModel } + , Cmd.map MailboxMsg subCmd + , Session.none + ) Route.Monitor -> ( { model | page = Monitor (Monitor.init model.session.host) } diff --git a/ui/src/Page/Mailbox.elm b/ui/src/Page/Mailbox.elm index ebd7ead..12c9bf9 100644 --- a/ui/src/Page/Mailbox.elm +++ b/ui/src/Page/Mailbox.elm @@ -3,6 +3,8 @@ 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.Relative as Relative import Json.Decode as Decode exposing (Decoder) import Html exposing (..) import Html.Attributes @@ -70,18 +72,22 @@ type alias Model = , state : State , bodyMode : Body , searchInput : String + , now : Date } -init : String -> Maybe MessageID -> Model +init : String -> Maybe MessageID -> ( Model, Cmd Msg ) init mailboxName selection = - Model mailboxName (LoadingList selection) SafeHtmlBody "" + ( Model mailboxName (LoadingList selection) SafeHtmlBody "" (Date.fromTime 0) + , load mailboxName + ) load : String -> Cmd Msg load mailboxName = Cmd.batch [ Ports.windowTitle (mailboxName ++ " - Inbucket") + , Task.perform Tick Time.now , getList mailboxName ] @@ -92,15 +98,22 @@ load mailboxName = subscriptions : Model -> Sub Msg subscriptions model = - case model.state of - ShowingList _ (ShowingMessage { message }) -> - if message.seen then - Sub.none - else - Time.every (250 * Time.millisecond) Tick + let + subSeen = + case model.state of + ShowingList _ (ShowingMessage { message }) -> + if message.seen then + Sub.none + else + Time.every (250 * Time.millisecond) SeenTick - _ -> - Sub.none + _ -> + Sub.none + in + Sub.batch + [ Time.every (30 * Time.second) Tick + , subSeen + ] @@ -119,6 +132,7 @@ type Msg | Purge | PurgeResult (Result Http.Error ()) | SearchInput String + | SeenTick Time | Tick Time | ViewMessage MessageID @@ -224,7 +238,7 @@ update session msg model = PurgeResult (Err err) -> ( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) ) - Tick now -> + SeenTick now -> case model.state of ShowingList _ (ShowingMessage { message, markSeenAt }) -> case markSeenAt of @@ -240,6 +254,9 @@ update session msg model = _ -> ( model, Cmd.none, Session.none ) + Tick now -> + ( { model | now = Date.fromTime now }, Cmd.none, Session.none ) + {-| Replace the currently displayed message. -} @@ -481,13 +498,13 @@ viewMessageList session model = (list |> filterMessageList |> List.reverse - |> List.map (messageChip list.selected) + |> List.map (messageChip model list.selected) ) ] -messageChip : Maybe MessageID -> MessageHeader -> Html Msg -messageChip selected message = +messageChip : Model -> Maybe MessageID -> MessageHeader -> Html Msg +messageChip model selected message = div [ classList [ ( "message-list-entry", True ) @@ -498,7 +515,7 @@ messageChip selected message = ] [ div [ class "subject" ] [ text message.subject ] , div [ class "from" ] [ text message.from ] - , div [ class "date" ] [ text message.date ] + , div [ class "date" ] [ relativeDate model message.date ] ] @@ -593,6 +610,11 @@ attachmentRow baseUrl attach = ] +relativeDate : Model -> Date -> Html Msg +relativeDate model date = + Relative.relativeTime model.now date |> text + + -- UTILITY diff --git a/ui/src/Page/Monitor.elm b/ui/src/Page/Monitor.elm index b70b71c..6e49aa4 100644 --- a/ui/src/Page/Monitor.elm +++ b/ui/src/Page/Monitor.elm @@ -2,6 +2,16 @@ 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 + ( format + , monthNameFirstThree + , dayOfMonthFixed + , hourNumber + , minuteFixed + , amPmUppercase + ) import Json.Decode exposing (decodeString) import Html exposing (..) import Html.Attributes exposing (..) @@ -32,8 +42,7 @@ init host = subscriptions : Model -> Sub Msg subscriptions model = - WebSocket.listen model.wsUrl - (decodeString MessageHeader.decoder >> NewMessage) + WebSocket.listen model.wsUrl (decodeString MessageHeader.decoder >> NewMessage) @@ -85,8 +94,27 @@ view session model = viewMessage : MessageHeader -> Html Msg viewMessage message = tr [ Events.onClick (OpenMessage message) ] - [ td [] [ text message.date ] + [ td [] [ text (timestamp message.date) ] , td [ class "desktop" ] [ text message.from ] , td [] [ text message.mailbox ] , td [] [ text message.subject ] ] + + + +-- UTILITY + + +timestamp : Date -> String +timestamp = + format + [ dayOfMonthFixed + , DateFormat.text "-" + , monthNameFirstThree + , DateFormat.text " " + , hourNumber + , DateFormat.text ":" + , minuteFixed + , DateFormat.text " " + , amPmUppercase + ]