From 8a3d291ff345ce35373352dfa909508d56734e9b Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 5 Apr 2020 15:30:08 -0700 Subject: [PATCH 1/3] ui: Improve layout menu function - Rename menu to mainMenu for clarity - Rename recent to recentMenu - Add a mouseOut timer to recentMenu --- ui/src/Layout.elm | 108 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 18 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index 3b0196f..43580c4 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -38,7 +38,9 @@ import Html.Attributes ) import Html.Events as Events import Modal +import Process import Route exposing (Route) +import Task {-| Used to highlight current page in navbar. @@ -52,17 +54,48 @@ type Page type alias Model msg = { mapMsg : Msg -> msg - , menuVisible : Bool - , recentVisible : Bool + , mainMenuVisible : Bool + , recentMenuVisible : Bool + , recentMenuTimer : Timer , mailboxName : String } +type Timer + = New + | Idle Int + | Timer Int + + +replaceTimer : Timer -> Timer +replaceTimer previous = + case previous of + New -> + Timer 0 + + Idle index -> + Timer (index + 1) + + Timer index -> + Timer (index + 1) + + +cancelTimer : Timer -> Timer +cancelTimer previous = + case previous of + Timer index -> + Idle (index + 1) + + _ -> + previous + + init : (Msg -> msg) -> Model msg init mapMsg = { mapMsg = mapMsg - , menuVisible = False - , recentVisible = False + , mainMenuVisible = False + , recentMenuVisible = False + , recentMenuTimer = New , mailboxName = "" } @@ -72,20 +105,24 @@ init mapMsg = reset : Model msg -> Model msg reset model = { model - | menuVisible = False - , recentVisible = False + | mainMenuVisible = False + , recentMenuVisible = False + , recentMenuTimer = cancelTimer model.recentMenuTimer , mailboxName = "" } type Msg = ClearFlash + | MainMenuToggled | ModalFocused Modal.Msg | ModalUnfocused | OnMailboxNameInput String | OpenMailbox - | ShowRecent Bool - | ToggleMenu + | RecentMenuMouseOver + | RecentMenuMouseOut + | RecentMenuTimeout Timer () + | RecentMenuToggled update : Msg -> Model msg -> Session -> ( Model msg, Session, Cmd msg ) @@ -97,6 +134,12 @@ update msg model session = , Cmd.none ) + MainMenuToggled -> + ( { model | mainMenuVisible = not model.mainMenuVisible } + , session + , Cmd.none + ) + ModalFocused message -> ( model , Modal.updateSession message session @@ -122,14 +165,43 @@ update msg model session = , Route.pushUrl session.key (Route.Mailbox model.mailboxName) ) - ShowRecent visible -> - ( { model | recentVisible = visible } + RecentMenuMouseOver -> + ( { model + | recentMenuVisible = True + , recentMenuTimer = cancelTimer model.recentMenuTimer + } , session , Cmd.none ) - ToggleMenu -> - ( { model | menuVisible = not model.menuVisible } + RecentMenuMouseOut -> + let + newTimer = + replaceTimer model.recentMenuTimer + in + ( { model + | recentMenuTimer = newTimer + } + , session + , Process.sleep 400 |> Task.perform (RecentMenuTimeout newTimer >> model.mapMsg) + ) + + RecentMenuTimeout timer _ -> + if timer == model.recentMenuTimer then + ( { model + | recentMenuVisible = False + , recentMenuTimer = cancelTimer timer + } + , session + , Cmd.none + ) + + else + -- Timer was no longer valid. + ( model, session, Cmd.none ) + + RecentMenuToggled -> + ( { model | recentMenuVisible = not model.recentMenuVisible } , session , Cmd.none ) @@ -150,11 +222,11 @@ frame { model, session, activePage, activeMailbox, modal, content } = div [ class "app" ] [ header [] [ nav [ class "navbar" ] - [ button [ class "navbar-toggle", Events.onClick (ToggleMenu |> model.mapMsg) ] + [ button [ class "navbar-toggle", Events.onClick (MainMenuToggled |> model.mapMsg) ] [ i [ class "fas fa-bars" ] [] ] , span [ class "navbar-brand" ] [ a [ Route.href Route.Home ] [ text "@ inbucket" ] ] - , ul [ class "main-nav", classList [ ( "active", model.menuVisible ) ] ] + , ul [ class "main-nav", classList [ ( "active", model.mainMenuVisible ) ] ] [ if session.config.monitorVisible then navbarLink Monitor Route.Monitor [ text "Monitor" ] activePage @@ -256,15 +328,15 @@ navbarRecent page activeMailbox model session = [ class "navbar-dropdown-container" , classList [ ( "navbar-active", active ) ] , attribute "aria-haspopup" "true" - , ariaExpanded model.recentVisible - , Events.onMouseOver (ShowRecent True |> model.mapMsg) - , Events.onMouseOut (ShowRecent False |> model.mapMsg) + , ariaExpanded model.recentMenuVisible + , Events.onMouseOver (RecentMenuMouseOver |> model.mapMsg) + , Events.onMouseOut (RecentMenuMouseOut |> model.mapMsg) ] [ span [ class "navbar-dropdown" ] [ text title , button [ class "navbar-dropdown-button" - , Events.onClick (ShowRecent (not model.recentVisible) |> model.mapMsg) + , Events.onClick (RecentMenuToggled |> model.mapMsg) ] [ i [ class "fas fa-chevron-down" ] [] ] ] From e8e506f870f332fc937bbe99b858a2cae663edbd Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 5 Apr 2020 16:22:16 -0700 Subject: [PATCH 2/3] ui: Refactor Timer into it's own module. --- ui/src/Layout.elm | 40 ++++++------------------------------- ui/src/Timer.elm | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 ui/src/Timer.elm diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index 43580c4..42d3ac3 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -41,6 +41,7 @@ import Modal import Process import Route exposing (Route) import Task +import Timer exposing (Timer) {-| Used to highlight current page in navbar. @@ -61,41 +62,12 @@ type alias Model msg = } -type Timer - = New - | Idle Int - | Timer Int - - -replaceTimer : Timer -> Timer -replaceTimer previous = - case previous of - New -> - Timer 0 - - Idle index -> - Timer (index + 1) - - Timer index -> - Timer (index + 1) - - -cancelTimer : Timer -> Timer -cancelTimer previous = - case previous of - Timer index -> - Idle (index + 1) - - _ -> - previous - - init : (Msg -> msg) -> Model msg init mapMsg = { mapMsg = mapMsg , mainMenuVisible = False , recentMenuVisible = False - , recentMenuTimer = New + , recentMenuTimer = Timer.empty , mailboxName = "" } @@ -107,7 +79,7 @@ reset model = { model | mainMenuVisible = False , recentMenuVisible = False - , recentMenuTimer = cancelTimer model.recentMenuTimer + , recentMenuTimer = Timer.cancel model.recentMenuTimer , mailboxName = "" } @@ -168,7 +140,7 @@ update msg model session = RecentMenuMouseOver -> ( { model | recentMenuVisible = True - , recentMenuTimer = cancelTimer model.recentMenuTimer + , recentMenuTimer = Timer.cancel model.recentMenuTimer } , session , Cmd.none @@ -177,7 +149,7 @@ update msg model session = RecentMenuMouseOut -> let newTimer = - replaceTimer model.recentMenuTimer + Timer.replace model.recentMenuTimer in ( { model | recentMenuTimer = newTimer @@ -190,7 +162,7 @@ update msg model session = if timer == model.recentMenuTimer then ( { model | recentMenuVisible = False - , recentMenuTimer = cancelTimer timer + , recentMenuTimer = Timer.cancel timer } , session , Cmd.none diff --git a/ui/src/Timer.elm b/ui/src/Timer.elm new file mode 100644 index 0000000..4fa640a --- /dev/null +++ b/ui/src/Timer.elm @@ -0,0 +1,51 @@ +module Timer exposing (Timer, cancel, empty, replace) + +{-| Implements an identity to track an asynchronous timer. +-} + + +type Timer + = Empty + | Idle Int + | Timer Int + + +empty : Timer +empty = + Empty + + +{-| Replaces the provided timer with a newly created one. +-} +replace : Timer -> Timer +replace previous = + case previous of + Empty -> + Timer 0 + + Idle index -> + Timer (next index) + + Timer index -> + Timer (next index) + + +{-| Cancels the provided timer without creating a replacement. +-} +cancel : Timer -> Timer +cancel previous = + case previous of + Timer index -> + Idle index + + _ -> + previous + + +next : Int -> Int +next index = + if index > 2 ^ 30 then + 0 + + else + index + 1 From 4fc8d229ebb35ec4e05ad2612e1e473950d5eb52 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 5 Apr 2020 17:27:22 -0700 Subject: [PATCH 3/3] ui: impl Timer.schedule function --- ui/src/Layout.elm | 6 +++--- ui/src/Timer.elm | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index 42d3ac3..e4ff701 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -93,7 +93,7 @@ type Msg | OpenMailbox | RecentMenuMouseOver | RecentMenuMouseOut - | RecentMenuTimeout Timer () + | RecentMenuTimeout Timer | RecentMenuToggled @@ -155,10 +155,10 @@ update msg model session = | recentMenuTimer = newTimer } , session - , Process.sleep 400 |> Task.perform (RecentMenuTimeout newTimer >> model.mapMsg) + , Timer.schedule (RecentMenuTimeout >> model.mapMsg) newTimer 400 ) - RecentMenuTimeout timer _ -> + RecentMenuTimeout timer -> if timer == model.recentMenuTimer then ( { model | recentMenuVisible = False diff --git a/ui/src/Timer.elm b/ui/src/Timer.elm index 4fa640a..fc82d44 100644 --- a/ui/src/Timer.elm +++ b/ui/src/Timer.elm @@ -1,9 +1,11 @@ -module Timer exposing (Timer, cancel, empty, replace) +module Timer exposing (Timer, cancel, empty, replace, schedule) + +import Process +import Task + {-| Implements an identity to track an asynchronous timer. -} - - type Timer = Empty | Idle Int @@ -15,6 +17,11 @@ empty = Empty +schedule : (Timer -> msg) -> Timer -> Float -> Cmd msg +schedule message timer millis = + Task.perform (always (message timer)) (Process.sleep millis) + + {-| Replaces the provided timer with a newly created one. -} replace : Timer -> Timer