1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 17:47:03 +00:00

ui: Refactor mailbox header list into State

This commit is contained in:
James Hillyerd
2018-11-04 16:01:17 -08:00
parent a8795f46dc
commit 852b9fce26
2 changed files with 112 additions and 84 deletions

View File

@@ -243,7 +243,7 @@ view model =
mailbox = mailbox =
case model.page of case model.page of
Mailbox subModel -> Mailbox subModel ->
subModel.name subModel.mailboxName
_ -> _ ->
"" ""

View File

@@ -25,45 +25,40 @@ type Body
type State type State
= NoSelection = LoadingList (Maybe MessageID)
| Selected MessageID | ShowingList (List MessageHeader) (Maybe MessageID)
| Viewing Visible | LoadingMessage (List MessageHeader) MessageID
| Transitioning Visible MessageID | ShowingMessage (List MessageHeader) VisibleMessage
| Transitioning (List MessageHeader) VisibleMessage MessageID
type alias MessageID = type alias MessageID =
String String
type alias Visible = type alias VisibleMessage =
{ message : Message { message : Message
, markSeenAt : Maybe Time , markSeenAt : Maybe Time
} }
type alias Model = type alias Model =
{ name : String { mailboxName : String
, state : State , state : State
, headers : List MessageHeader
, bodyMode : Body , bodyMode : Body
} }
init : String -> Maybe MessageID -> Model init : String -> Maybe MessageID -> Model
init name selection = init mailboxName selection =
case selection of Model mailboxName (LoadingList selection) SafeHtmlBody
Just id ->
Model name (Selected id) [] SafeHtmlBody
Nothing ->
Model name NoSelection [] SafeHtmlBody
load : String -> Cmd Msg load : String -> Cmd Msg
load name = load mailboxName =
Cmd.batch Cmd.batch
[ Ports.windowTitle (name ++ " - Inbucket") [ Ports.windowTitle (mailboxName ++ " - Inbucket")
, getMailbox name , getList mailboxName
] ]
@@ -74,7 +69,7 @@ load name =
subscriptions : Model -> Sub Msg subscriptions : Model -> Sub Msg
subscriptions model = subscriptions model =
case model.state of case model.state of
Viewing { message } -> ShowingMessage _ { message } ->
if message.seen then if message.seen then
Sub.none Sub.none
else else
@@ -93,7 +88,7 @@ type Msg
| ViewMessage MessageID | ViewMessage MessageID
| DeleteMessage Message | DeleteMessage Message
| DeleteMessageResult (Result Http.Error ()) | DeleteMessageResult (Result Http.Error ())
| MailboxResult (Result Http.Error (List MessageHeader)) | ListResult (Result Http.Error (List MessageHeader))
| MarkSeenResult (Result Http.Error ()) | MarkSeenResult (Result Http.Error ())
| MessageResult (Result Http.Error Message) | MessageResult (Result Http.Error Message)
| MessageBody Body | MessageBody Body
@@ -107,16 +102,16 @@ update session msg model =
ClickMessage id -> ClickMessage id ->
( updateSelected model id ( updateSelected model id
, Cmd.batch , Cmd.batch
[ Route.newUrl (Route.Message model.name id) [ Route.newUrl (Route.Message model.mailboxName id)
, getMessage model.name id , getMessage model.mailboxName id
] ]
, Session.DisableRouting , Session.DisableRouting
) )
ViewMessage id -> ViewMessage id ->
( updateSelected model id ( updateSelected model id
, getMessage model.name id , getMessage model.mailboxName id
, Session.AddRecent model.name , Session.AddRecent model.mailboxName
) )
DeleteMessage msg -> DeleteMessage msg ->
@@ -128,20 +123,25 @@ update session msg model =
DeleteMessageResult (Err err) -> DeleteMessageResult (Err err) ->
( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) ) ( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) )
MailboxResult (Ok headers) -> ListResult (Ok headers) ->
case model.state of
LoadingList selection ->
let let
newModel = newModel =
{ model | headers = headers } { model | state = ShowingList headers selection }
in in
case model.state of case selection of
Selected id -> Just id ->
-- Recurse to select message id. -- Recurse to select message id.
update session (ViewMessage id) newModel update session (ViewMessage id) newModel
_ -> Nothing ->
( newModel, Cmd.none, Session.AddRecent model.name ) ( newModel, Cmd.none, Session.AddRecent model.mailboxName )
MailboxResult (Err err) -> _ ->
( model, Cmd.none, Session.none )
ListResult (Err err) ->
( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) ) ( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) )
MarkSeenResult (Ok _) -> MarkSeenResult (Ok _) ->
@@ -157,14 +157,31 @@ update session msg model =
TextBody TextBody
else else
model.bodyMode model.bodyMode
in
updateMessage list message =
( { model ( { model
| state = Viewing { message = msg, markSeenAt = Nothing } | state = ShowingMessage list { message = message, markSeenAt = Nothing }
, bodyMode = bodyMode , bodyMode = bodyMode
} }
, Task.perform OpenedTime Time.now , Task.perform OpenedTime Time.now
, Session.none , Session.none
) )
in
case model.state of
LoadingList _ ->
( model, Cmd.none, Session.none )
ShowingList list _ ->
updateMessage list msg
LoadingMessage list _ ->
updateMessage list msg
ShowingMessage list _ ->
updateMessage list msg
Transitioning list _ _ ->
updateMessage list msg
MessageResult (Err err) -> MessageResult (Err err) ->
( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) ) ( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) )
@@ -174,14 +191,14 @@ update session msg model =
OpenedTime time -> OpenedTime time ->
case model.state of case model.state of
Viewing visible -> ShowingMessage list visible ->
if visible.message.seen then if visible.message.seen then
( model, Cmd.none, Session.none ) ( model, Cmd.none, Session.none )
else else
-- Set delay to report message as seen to backend. -- Set delay to report message as seen to backend.
( { model ( { model
| state = | state =
Viewing ShowingMessage list
{ visible { visible
| markSeenAt = Just (time + (1.5 * Time.second)) | markSeenAt = Just (time + (1.5 * Time.second))
} }
@@ -195,7 +212,7 @@ update session msg model =
Tick now -> Tick now ->
case model.state of case model.state of
Viewing { message, markSeenAt } -> ShowingMessage _ { message, markSeenAt } ->
case markSeenAt of case markSeenAt of
Just deadline -> Just deadline ->
if now >= deadline then if now >= deadline then
@@ -213,22 +230,28 @@ update session msg model =
updateSelected : Model -> MessageID -> Model updateSelected : Model -> MessageID -> Model
updateSelected model id = updateSelected model id =
case model.state of case model.state of
Viewing visible -> ShowingList list _ ->
{ model | state = LoadingMessage list id }
ShowingMessage list visible ->
-- Use Transitioning state to prevent message flicker. -- Use Transitioning state to prevent message flicker.
{ model | state = Transitioning visible id } { model | state = Transitioning list visible id }
Transitioning list visible _ ->
{ model | state = Transitioning list visible id }
_ -> _ ->
{ model | state = Selected id } model
getMailbox : String -> Cmd Msg getList : String -> Cmd Msg
getMailbox name = getList mailboxName =
let let
url = url =
"/api/v1/mailbox/" ++ name "/api/v1/mailbox/" ++ mailboxName
in in
Http.get url (Decode.list MessageHeader.decoder) Http.get url (Decode.list MessageHeader.decoder)
|> Http.send MailboxResult |> Http.send ListResult
deleteMessage : Model -> Message -> ( Model, Cmd Msg, Session.Msg ) deleteMessage : Model -> Message -> ( Model, Cmd Msg, Session.Msg )
@@ -241,20 +264,22 @@ deleteMessage model msg =
HttpUtil.delete url HttpUtil.delete url
|> Http.send DeleteMessageResult |> Http.send DeleteMessageResult
in in
( { model case model.state of
| state = NoSelection ShowingMessage list _ ->
, headers = List.filter (\x -> x.id /= msg.id) model.headers ( { model | state = ShowingList (List.filter (\x -> x.id /= msg.id) list) Nothing }
}
, cmd , cmd
, Session.none , Session.none
) )
_ ->
( model, cmd, Session.none )
getMessage : String -> MessageID -> Cmd Msg getMessage : String -> MessageID -> Cmd Msg
getMessage mailbox id = getMessage mailboxName id =
let let
url = url =
"/serve/m/" ++ mailbox ++ "/" ++ id "/serve/m/" ++ mailboxName ++ "/" ++ id
in in
Http.get url Message.decoder Http.get url Message.decoder
|> Http.send MessageResult |> Http.send MessageResult
@@ -263,7 +288,7 @@ getMessage mailbox id =
markMessageSeen : Model -> Message -> ( Model, Cmd Msg, Session.Msg ) markMessageSeen : Model -> Message -> ( Model, Cmd Msg, Session.Msg )
markMessageSeen model message = markMessageSeen model message =
case model.state of case model.state of
Viewing visible -> ShowingMessage list visible ->
let let
message = message =
visible.message visible.message
@@ -287,12 +312,11 @@ markMessageSeen model message =
in in
( { model ( { model
| state = | state =
Viewing ShowingMessage (List.map updateSeen list)
{ visible { visible
| message = { message | seen = True } | message = { message | seen = True }
, markSeenAt = Nothing , markSeenAt = Nothing
} }
, headers = List.map updateSeen model.headers
} }
, command , command
, Session.None , Session.None
@@ -309,20 +333,36 @@ markMessageSeen model message =
view : Session -> Model -> Html Msg view : Session -> Model -> Html Msg
view session model = view session model =
div [ id "page", class "mailbox" ] div [ id "page", class "mailbox" ]
[ aside [ id "message-list" ] [ messageList model ] [ aside [ id "message-list" ]
[ case model.state of
LoadingList _ ->
messageList [] Nothing
ShowingList list selection ->
messageList list selection
LoadingMessage list selection ->
messageList list (Just selection)
ShowingMessage list visible ->
messageList list (Just visible.message.id)
Transitioning list _ selection ->
messageList list (Just selection)
]
, main_ , main_
[ id "message" ] [ id "message" ]
[ case model.state of [ case model.state of
NoSelection -> ShowingList _ _ ->
text text
("Select a message on the left," ("Select a message on the left,"
++ " or enter a different username into the box on upper right." ++ " or enter a different username into the box on upper right."
) )
Viewing { message } -> ShowingMessage _ { message } ->
viewMessage message model.bodyMode viewMessage message model.bodyMode
Transitioning { message } _ -> Transitioning _ { message } _ ->
viewMessage message model.bodyMode viewMessage message model.bodyMode
_ -> _ ->
@@ -331,21 +371,9 @@ view session model =
] ]
messageList : Model -> Html Msg messageList : List MessageHeader -> Maybe MessageID -> Html Msg
messageList model = messageList list selected =
let div [] (List.map (messageChip selected) (List.reverse list))
selected =
case model.state of
Selected id ->
Just id
Viewing { message } ->
Just message.id
_ ->
Nothing
in
div [] (List.map (messageChip selected) (List.reverse model.headers))
messageChip : Maybe MessageID -> MessageHeader -> Html Msg messageChip : Maybe MessageID -> MessageHeader -> Html Msg