mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
ui: Friendly date format for mailbox list, monitor
This commit is contained in:
@@ -29,7 +29,8 @@
|
|||||||
"elm-lang/svg": "2.0.0 <= v < 3.0.0",
|
"elm-lang/svg": "2.0.0 <= v < 3.0.0",
|
||||||
"elm-lang/websocket": "1.0.2 <= v < 2.0.0",
|
"elm-lang/websocket": "1.0.2 <= v < 2.0.0",
|
||||||
"evancz/url-parser": "2.0.1 <= v < 3.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"
|
"elm-version": "0.18.0 <= v < 0.19.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module Data.MessageHeader exposing (..)
|
module Data.MessageHeader exposing (..)
|
||||||
|
|
||||||
|
import Date exposing (Date)
|
||||||
import Json.Decode as Decode exposing (..)
|
import Json.Decode as Decode exposing (..)
|
||||||
import Json.Decode.Pipeline exposing (..)
|
import Json.Decode.Pipeline exposing (..)
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ type alias MessageHeader =
|
|||||||
, from : String
|
, from : String
|
||||||
, to : List String
|
, to : List String
|
||||||
, subject : String
|
, subject : String
|
||||||
, date : String
|
, date : Date
|
||||||
, size : Int
|
, size : Int
|
||||||
, seen : Bool
|
, seen : Bool
|
||||||
}
|
}
|
||||||
@@ -24,6 +25,21 @@ decoder =
|
|||||||
|> optional "from" string ""
|
|> optional "from" string ""
|
||||||
|> required "to" (list string)
|
|> required "to" (list string)
|
||||||
|> optional "subject" string ""
|
|> optional "subject" string ""
|
||||||
|> required "date" string
|
|> required "date" date
|
||||||
|> required "size" int
|
|> required "size" int
|
||||||
|> required "seen" bool
|
|> 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
|
||||||
|
|||||||
@@ -188,16 +188,24 @@ setRoute route model =
|
|||||||
)
|
)
|
||||||
|
|
||||||
Route.Mailbox name ->
|
Route.Mailbox name ->
|
||||||
( { model | page = Mailbox (Mailbox.init name Nothing) }
|
let
|
||||||
, Cmd.map MailboxMsg (Mailbox.load name)
|
( subModel, subCmd ) =
|
||||||
, Session.none
|
Mailbox.init name Nothing
|
||||||
)
|
in
|
||||||
|
( { model | page = Mailbox subModel }
|
||||||
|
, Cmd.map MailboxMsg subCmd
|
||||||
|
, Session.none
|
||||||
|
)
|
||||||
|
|
||||||
Route.Message mailbox id ->
|
Route.Message mailbox id ->
|
||||||
( { model | page = Mailbox (Mailbox.init mailbox (Just id)) }
|
let
|
||||||
, Cmd.map MailboxMsg (Mailbox.load mailbox)
|
( subModel, subCmd ) =
|
||||||
, Session.none
|
Mailbox.init mailbox (Just id)
|
||||||
)
|
in
|
||||||
|
( { model | page = Mailbox subModel }
|
||||||
|
, Cmd.map MailboxMsg subCmd
|
||||||
|
, Session.none
|
||||||
|
)
|
||||||
|
|
||||||
Route.Monitor ->
|
Route.Monitor ->
|
||||||
( { model | page = Monitor (Monitor.init model.session.host) }
|
( { model | page = Monitor (Monitor.init model.session.host) }
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ module Page.Mailbox exposing (Model, Msg, init, load, subscriptions, update, vie
|
|||||||
import Data.Message as Message exposing (Message)
|
import Data.Message as Message exposing (Message)
|
||||||
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
||||||
import Data.Session as Session exposing (Session)
|
import Data.Session as Session exposing (Session)
|
||||||
|
import Date exposing (Date)
|
||||||
|
import DateFormat.Relative as Relative
|
||||||
import Json.Decode as Decode exposing (Decoder)
|
import Json.Decode as Decode exposing (Decoder)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes
|
import Html.Attributes
|
||||||
@@ -70,18 +72,22 @@ type alias Model =
|
|||||||
, state : State
|
, state : State
|
||||||
, bodyMode : Body
|
, bodyMode : Body
|
||||||
, searchInput : String
|
, searchInput : String
|
||||||
|
, now : Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
init : String -> Maybe MessageID -> Model
|
init : String -> Maybe MessageID -> ( Model, Cmd Msg )
|
||||||
init mailboxName selection =
|
init mailboxName selection =
|
||||||
Model mailboxName (LoadingList selection) SafeHtmlBody ""
|
( Model mailboxName (LoadingList selection) SafeHtmlBody "" (Date.fromTime 0)
|
||||||
|
, load mailboxName
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
load : String -> Cmd Msg
|
load : String -> Cmd Msg
|
||||||
load mailboxName =
|
load mailboxName =
|
||||||
Cmd.batch
|
Cmd.batch
|
||||||
[ Ports.windowTitle (mailboxName ++ " - Inbucket")
|
[ Ports.windowTitle (mailboxName ++ " - Inbucket")
|
||||||
|
, Task.perform Tick Time.now
|
||||||
, getList mailboxName
|
, getList mailboxName
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -92,15 +98,22 @@ load mailboxName =
|
|||||||
|
|
||||||
subscriptions : Model -> Sub Msg
|
subscriptions : Model -> Sub Msg
|
||||||
subscriptions model =
|
subscriptions model =
|
||||||
case model.state of
|
let
|
||||||
ShowingList _ (ShowingMessage { message }) ->
|
subSeen =
|
||||||
if message.seen then
|
case model.state of
|
||||||
Sub.none
|
ShowingList _ (ShowingMessage { message }) ->
|
||||||
else
|
if message.seen then
|
||||||
Time.every (250 * Time.millisecond) Tick
|
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
|
| Purge
|
||||||
| PurgeResult (Result Http.Error ())
|
| PurgeResult (Result Http.Error ())
|
||||||
| SearchInput String
|
| SearchInput String
|
||||||
|
| SeenTick Time
|
||||||
| Tick Time
|
| Tick Time
|
||||||
| ViewMessage MessageID
|
| ViewMessage MessageID
|
||||||
|
|
||||||
@@ -224,7 +238,7 @@ update session msg model =
|
|||||||
PurgeResult (Err err) ->
|
PurgeResult (Err err) ->
|
||||||
( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) )
|
( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) )
|
||||||
|
|
||||||
Tick now ->
|
SeenTick now ->
|
||||||
case model.state of
|
case model.state of
|
||||||
ShowingList _ (ShowingMessage { message, markSeenAt }) ->
|
ShowingList _ (ShowingMessage { message, markSeenAt }) ->
|
||||||
case markSeenAt of
|
case markSeenAt of
|
||||||
@@ -240,6 +254,9 @@ update session msg model =
|
|||||||
_ ->
|
_ ->
|
||||||
( model, Cmd.none, Session.none )
|
( model, Cmd.none, Session.none )
|
||||||
|
|
||||||
|
Tick now ->
|
||||||
|
( { model | now = Date.fromTime now }, Cmd.none, Session.none )
|
||||||
|
|
||||||
|
|
||||||
{-| Replace the currently displayed message.
|
{-| Replace the currently displayed message.
|
||||||
-}
|
-}
|
||||||
@@ -481,13 +498,13 @@ viewMessageList session model =
|
|||||||
(list
|
(list
|
||||||
|> filterMessageList
|
|> filterMessageList
|
||||||
|> List.reverse
|
|> List.reverse
|
||||||
|> List.map (messageChip list.selected)
|
|> List.map (messageChip model list.selected)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
messageChip : Maybe MessageID -> MessageHeader -> Html Msg
|
messageChip : Model -> Maybe MessageID -> MessageHeader -> Html Msg
|
||||||
messageChip selected message =
|
messageChip model selected message =
|
||||||
div
|
div
|
||||||
[ classList
|
[ classList
|
||||||
[ ( "message-list-entry", True )
|
[ ( "message-list-entry", True )
|
||||||
@@ -498,7 +515,7 @@ messageChip selected message =
|
|||||||
]
|
]
|
||||||
[ div [ class "subject" ] [ text message.subject ]
|
[ div [ class "subject" ] [ text message.subject ]
|
||||||
, div [ class "from" ] [ text message.from ]
|
, 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
|
-- UTILITY
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,16 @@ module Page.Monitor exposing (Model, Msg, init, subscriptions, update, view)
|
|||||||
|
|
||||||
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
||||||
import Data.Session as Session exposing (Session)
|
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 Json.Decode exposing (decodeString)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
@@ -32,8 +42,7 @@ init host =
|
|||||||
|
|
||||||
subscriptions : Model -> Sub Msg
|
subscriptions : Model -> Sub Msg
|
||||||
subscriptions model =
|
subscriptions model =
|
||||||
WebSocket.listen model.wsUrl
|
WebSocket.listen model.wsUrl (decodeString MessageHeader.decoder >> NewMessage)
|
||||||
(decodeString MessageHeader.decoder >> NewMessage)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -85,8 +94,27 @@ view session model =
|
|||||||
viewMessage : MessageHeader -> Html Msg
|
viewMessage : MessageHeader -> Html Msg
|
||||||
viewMessage message =
|
viewMessage message =
|
||||||
tr [ Events.onClick (OpenMessage message) ]
|
tr [ Events.onClick (OpenMessage message) ]
|
||||||
[ td [] [ text message.date ]
|
[ td [] [ text (timestamp message.date) ]
|
||||||
, td [ class "desktop" ] [ text message.from ]
|
, td [ class "desktop" ] [ text message.from ]
|
||||||
, td [] [ text message.mailbox ]
|
, td [] [ text message.mailbox ]
|
||||||
, td [] [ text message.subject ]
|
, td [] [ text message.subject ]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UTILITY
|
||||||
|
|
||||||
|
|
||||||
|
timestamp : Date -> String
|
||||||
|
timestamp =
|
||||||
|
format
|
||||||
|
[ dayOfMonthFixed
|
||||||
|
, DateFormat.text "-"
|
||||||
|
, monthNameFirstThree
|
||||||
|
, DateFormat.text " "
|
||||||
|
, hourNumber
|
||||||
|
, DateFormat.text ":"
|
||||||
|
, minuteFixed
|
||||||
|
, DateFormat.text " "
|
||||||
|
, amPmUppercase
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user