on master help sign in
Got menu working
9aeb088iko
frontend/Main.elm+32−75
@@ -34,8 +34,6 @@ type Msg
34 | ShowCipherPage Bridge.Sub_LoadCipher
34 | ShowCipherPage Bridge.Sub_LoadCipher
35 | RequestCipher CipherId
35 | RequestCipher CipherId
36 | PopView
36 | PopView
37− | ToggleMenu
38− | ChangeFilter CipherFilter
39
37
40
38
41type alias CipherId =
39type alias CipherId =
@@ -45,9 +43,6 @@ type alias CipherId =
45type alias Model =
43type alias Model =
46 { notifications : List Notification.Config
44 { notifications : List Notification.Config
47 , pageStack : Navigation.PageStack PageModel
45 , pageStack : Navigation.PageStack PageModel
48− , menuShown : Bool
49− , email : Maybe String
50− , filter : CipherFilter
51 }
46 }
52
47
53
48
@@ -64,7 +59,13 @@ type PageModel
64 | CipherModel Cipher.Model
59 | CipherModel Cipher.Model
65
60
66
61
62+showPage :
63+ PageModel
64+ ->
65+ { title : String
67−showPage : PageModel -> ( String, List (Html Msg) )
66+ , body : List (Html Msg)
67+ , topButton : Maybe (Navigation.TopButton Msg)
68+ }
68showPage page =
69showPage page =
69 case page of
70 case page of
70 LoginModel model ->
71 LoginModel model ->
@@ -72,31 +73,50 @@ showPage page =
72 p =
73 p =
73 Login.page loginCallbacks LoginMsg
74 Login.page loginCallbacks LoginMsg
74 in
75 in
75− ( p.title model, p.view model )
76+ { title = p.title model
77+ , body = p.view model
78+ , topButton = Nothing
79+ }
76
80
77 CiphersModel model ->
81 CiphersModel model ->
78 let
82 let
79 p =
83 p =
80 Ciphers.page ciphersCallbacks CiphersMsg
84 Ciphers.page ciphersCallbacks CiphersMsg
81 in
85 in
82− ( p.title model, p.view model )
86+ { title = p.title model
87+ , body = p.view model
88+ , topButton =
89+ Ciphers.menuConfig model
90+ |> Navigation.mapMenuConfig CiphersMsg
91+ |> Navigation.MenuButton
92+ |> Just
93+ }
83
94
84 LoadingPage ->
95 LoadingPage ->
96+ { title = ""
85− ( "", [ loader ] )
97+ , body = [ loader ]
98+ , topButton = Nothing
99+ }
86
100
87 MasterPasswordModel model ->
101 MasterPasswordModel model ->
88 let
102 let
89 p =
103 p =
90 MasterPassword.page masterPasswordCallbacks MasterPasswordMsg
104 MasterPassword.page masterPasswordCallbacks MasterPasswordMsg
91 in
105 in
92− ( p.title model, p.view model )
106+ { title = p.title model
107+ , body = p.view model
108+ , topButton = Nothing
109+ }
93
110
94 CipherModel model ->
111 CipherModel model ->
95 let
112 let
96 p =
113 p =
97 Cipher.page cipherCallbacks CipherMsg
114 Cipher.page cipherCallbacks CipherMsg
98 in
115 in
99− ( p.title model, p.view model )
116+ { title = p.title model
117+ , body = p.view model
118+ , topButton = Nothing
119+ }
100
120
101
121
102main : Program () Model Msg
122main : Program () Model Msg
@@ -137,78 +157,18 @@ subscriptions _ =
137 )
157 )
138
158
139
159
140−type CipherFilter
141− = AnyCipher
142− | Specific Bridge.Sub_LoadCiphers_cipherType
143−
144−
145−applyCipherFilter : CipherFilter -> Bridge.Sub_LoadCiphers_List -> Bridge.Sub_LoadCiphers_List
146−applyCipherFilter filter list =
147− case filter of
148− AnyCipher ->
149− list
150−
151− Specific t ->
152− list |> List.filter (.cipherType >> (==) t)
153−
154−
155init : ( Model, Cmd Msg )
160init : ( Model, Cmd Msg )
156init =
161init =
157 ( { notifications = []
162 ( { notifications = []
158 , pageStack = Nonempty.singleton LoadingPage
163 , pageStack = Nonempty.singleton LoadingPage
159− , menuShown = False
160− , email = Nothing
161− , filter = AnyCipher
162 }
164 }
163 , FFI.sendBridge Bridge.Init
165 , FFI.sendBridge Bridge.Init
164 )
166 )
165
167
166
168
167−pageShouldShowMenu : PageModel -> Bool
168−pageShouldShowMenu page =
169− case page of
170− CiphersModel _ ->
171− True
172−
173− _ ->
174− False
175−
176−
177view : Model -> Html Msg
169view : Model -> Html Msg
178view model =
170view model =
179− Navigation.showNavigationView
171+ Navigation.showNavigationView model.pageStack showPage
180− { popStack = PopView
181− , toggleMenu = ToggleMenu
182− }
183− model.pageStack
184− (\page ->
185− let
186− ( title, body ) =
187− case page of
188− CiphersModel m ->
189− CiphersModel { m | ciphers = m.ciphers |> applyCipherFilter model.filter }
190− |> showPage
191−
192− _ ->
193− showPage page
194− in
195− { title = title
196− , body = maybeList (List.head model.notifications) (Notification.notification CloseNotification) ++ body
197− , menuConfig =
198− if pageShouldShowMenu page && model.menuShown then
199− Just
200− { title = Maybe.withDefault "" model.email
201− , items =
202− [ { icons = "", name = "", trigger = ChangeFilter AnyCipher }
203− ]
204− , close = ToggleMenu
205− }
206−
207− else
208− Nothing
209− , menuPossible = pageShouldShowMenu page
210− }
211− )
212
172
213
173
214mapHead : (a -> a) -> Nonempty a -> Nonempty a
174mapHead : (a -> a) -> Nonempty a -> Nonempty a
@@ -357,9 +317,6 @@ update msg model =
357 , Cmd.none
317 , Cmd.none
358 )
318 )
359
319
360− ToggleMenu ->
361− ( { model | menuShown = not model.menuShown }, Cmd.none )
362−
363
320
364loginCallbacks : Login.Callbacks Msg
321loginCallbacks : Login.Callbacks Msg
365loginCallbacks =
322loginCallbacks =
frontend/Pages/Ciphers.elm+88−4
@@ -1,10 +1,11 @@
1−module Pages.Ciphers exposing (Callbacks, Model, Msg, page)
1+module Pages.Ciphers exposing (Callbacks, Model, Msg, menuConfig, page)
2
2
3import Bridge
3import Bridge
4import Html exposing (..)
4import Html exposing (..)
5import Html.Attributes as Attr
5import Html.Attributes as Attr
6import Html.Events as Ev
6import Html.Events as Ev
7import Page exposing (..)
7import Page exposing (..)
8+import Pages.Navigation as Navigation
8import Search
9import Search
9import Utils exposing (..)
10import Utils exposing (..)
10
11
@@ -12,12 +13,66 @@ import Utils exposing (..)
12type alias Model =
13type alias Model =
13 { ciphers : Bridge.Sub_LoadCiphers_List
14 { ciphers : Bridge.Sub_LoadCiphers_List
14 , search : String
15 , search : String
16+ , menuVisible : Bool
17+ , ciphersListFilter : CiphersFilter
18+ }
19+
20+
21+type CiphersFilter
22+ = AllCiphers
23+ | SpecificCiphers Bridge.Sub_LoadCiphers_cipherType
24+
25+
26+applyCipherFilter : CiphersFilter -> Bridge.Sub_LoadCiphers_List -> Bridge.Sub_LoadCiphers_List
27+applyCipherFilter filter list =
28+ case filter of
29+ AllCiphers ->
30+ list
31+
32+ SpecificCiphers t ->
33+ list |> List.filter (.cipherType >> (==) t)
34+
35+
36+menuConfig : Model -> Navigation.MenuConfig Msg
37+menuConfig { ciphersListFilter, menuVisible } =
38+ { title = "TODO"
39+ , items =
40+ [ { icon = "units"
41+ , name = "All items"
42+ , trigger = UpdateFilter AllCiphers
43+ , current = ciphersListFilter == AllCiphers
44+ }
45+ , { icon = "security"
46+ , name = "Passwords"
47+ , trigger = UpdateFilter (SpecificCiphers Bridge.LoginType)
48+ , current = ciphersListFilter == SpecificCiphers Bridge.LoginType
49+ }
50+ , { icon = "edit"
51+ , name = "Notes"
52+ , trigger = UpdateFilter (SpecificCiphers Bridge.NoteType)
53+ , current = ciphersListFilter == SpecificCiphers Bridge.NoteType
54+ }
55+ , { icon = "containers"
56+ , name = "Cards"
57+ , trigger = UpdateFilter (SpecificCiphers Bridge.CardType)
58+ , current = ciphersListFilter == SpecificCiphers Bridge.CardType
59+ }
60+ , { icon = "user"
61+ , name = "Identities"
62+ , trigger = UpdateFilter (SpecificCiphers Bridge.IdentityType)
63+ , current = ciphersListFilter == SpecificCiphers Bridge.IdentityType
64+ }
65+ ]
66+ , toggle = ToggleMenu
67+ , visible = menuVisible
15 }
68 }
16
69
17
70
18type Msg
71type Msg
19 = Noop
72 = Noop
20 | UpdateSearch String
73 | UpdateSearch String
74+ | ToggleMenu
75+ | UpdateFilter CiphersFilter
21
76
22
77
23type alias Callbacks msg =
78type alias Callbacks msg =
@@ -31,13 +86,35 @@ page callbacks liftMsg =
31 , view = view callbacks liftMsg
86 , view = view callbacks liftMsg
32 , update = \msg model -> update callbacks liftMsg msg model
87 , update = \msg model -> update callbacks liftMsg msg model
33 , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
88 , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
89+ , title =
90+ \{ ciphersListFilter } ->
91+ case ciphersListFilter of
92+ AllCiphers ->
93+ "All items"
94+
95+ SpecificCiphers cipherType ->
96+ case cipherType of
97+ Bridge.CardType ->
98+ "Cards"
99+
100+ Bridge.IdentityType ->
101+ "Identities"
102+
103+ Bridge.LoginType ->
34− , title = always "Passwords"
104+ "Passwords"
105+
106+ Bridge.NoteType ->
107+ "Notes"
35 }
108 }
36
109
37
110
38init : Bridge.Sub_LoadCiphers_List -> ( Model, Cmd Msg )
111init : Bridge.Sub_LoadCiphers_List -> ( Model, Cmd Msg )
39init ciphers =
112init ciphers =
40− ( { ciphers = ciphers, search = "" }
113+ ( { ciphers = ciphers
114+ , search = ""
115+ , menuVisible = False
116+ , ciphersListFilter = AllCiphers
117+ }
41 , Cmd.none
118 , Cmd.none
42 )
119 )
43
120
@@ -51,6 +128,12 @@ update {} liftMsg msg model =
51 UpdateSearch s ->
128 UpdateSearch s ->
52 ( Ok { model | search = s }, Cmd.none )
129 ( Ok { model | search = s }, Cmd.none )
53
130
131+ ToggleMenu ->
132+ ( Ok { model | menuVisible = not model.menuVisible }, Cmd.none )
133+
134+ UpdateFilter filter ->
135+ ( Ok { model | ciphersListFilter = filter, menuVisible = False }, Cmd.none )
136+
54
137
55subscriptions : Model -> Sub Msg
138subscriptions : Model -> Sub Msg
56subscriptions model =
139subscriptions model =
@@ -58,7 +141,7 @@ subscriptions model =
58
141
59
142
60view : Callbacks emsg -> (Msg -> emsg) -> Model -> List (Html emsg)
143view : Callbacks emsg -> (Msg -> emsg) -> Model -> List (Html emsg)
61−view { selected } liftMsg { ciphers, search } =
144+view { selected } liftMsg { ciphers, search, ciphersListFilter } =
62 [ input
145 [ input
63 [ Attr.type_ "search"
146 [ Attr.type_ "search"
64 , Attr.value search
147 , Attr.value search
@@ -69,6 +152,7 @@ view { selected } liftMsg { ciphers, search } =
69 []
152 []
70 , ul [ Attr.class "p-list--divided" ]
153 , ul [ Attr.class "p-list--divided" ]
71 (ciphers
154 (ciphers
155+ |> applyCipherFilter ciphersListFilter
72 |> Search.searchList (cipherSearch search)
156 |> Search.searchList (cipherSearch search)
73 |> List.map
157 |> List.map
74 (\{ name, date, id } ->
158 (\{ name, date, id } ->
frontend/Pages/Navigation.elm+69−70
@@ -1,7 +1,9 @@
1module Pages.Navigation exposing
1module Pages.Navigation exposing
2 ( Config
2 ( Config
3+ , MenuConfig
3 , PageStack
4 , PageStack
4− , menu
5+ , TopButton(..)
6+ , mapMenuConfig
5 , popView
7 , popView
6 , pushView
8 , pushView
7 , showNavigationView
9 , showNavigationView
@@ -14,78 +16,91 @@ import List.Nonempty as Nonempty exposing (Nonempty)
14import Utils exposing (..)
16import Utils exposing (..)
15
17
16
18
17−type TopButton
19+type TopButton msg
18− = BackButton
20+ = BackButton msg
19− | MenuButton
21+ | MenuButton (MenuConfig msg)
20
22
21
23
22type alias Config msg =
24type alias Config msg =
23− { topButton : Maybe TopButton
25+ { topButton : Maybe (TopButton msg)
24 , title : String
26 , title : String
25− , pop : msg
26− , toggleMenu : msg
27 }
27 }
28
28
29
29
30−navigation : Config msg -> Html msg
30+navigation : Config msg -> List (Html msg)
31−navigation { topButton, title, pop, toggleMenu } =
31+navigation { topButton, title } =
32− nav [ Attr.class "p-tabs p-tabs__list" ]
33− [ h3 [ Attr.class "u-no-margin u-no-padding" ]
34− [ button
35− (Attr.class "p-button--base is-inline u-no-margin" :: optional (Nothing == topButton) (Attr.style "visibility" "hidden"))
36− [ i
37− [ Attr.class
38− (case topButton of
32+ (case topButton of
39− Just BackButton ->
33+ Just (MenuButton cfg) ->
40− "p-icon--chevron-up ninety-counter"
34+ optional cfg.visible (menu cfg)
41
35
42− Just MenuButton ->
36+ _ ->
43− "p-icon--menu"
37+ []
44−
38+ )
45− Nothing ->
39+ ++ [ nav [ Attr.class "p-tabs p-tabs__list" ]
46− ""
40+ [ h3 [ Attr.class "u-no-margin u-no-padding" ]
47− )
48− , Ev.onClick
49− (case topButton of
41+ [ case topButton of
50− Nothing ->
42+ Nothing ->
51− pop
43+ button
52−
44+ [ Attr.class "p-button--base is-inline u-no-margin", Attr.style "visibility" "hidden" ]
45+ []
46+
53− Just BackButton ->
47+ Just (BackButton msg) ->
54− pop
48+ button
55−
49+ [ Attr.class "p-button--base is-inline u-no-margin", Ev.onClick msg ]
50+ [ i [ Attr.class "p-icon--chevron-up ninety-counter" ] [] ]
51+
56− Just MenuButton ->
52+ Just (MenuButton cfg) ->
57− toggleMenu
53+ button
58− )
54+ [ Attr.class "p-button--base is-inline u-no-margin", Ev.onClick cfg.toggle ]
55+ [ i [ Attr.class "p-icon--menu" ] [] ]
56+ , text title
59 ]
57 ]
60− []
61 ]
58 ]
62− , text title
63− ]
64− ]
59+ ]
65
60
66
61
67type alias MenuConfig msg =
62type alias MenuConfig msg =
68 { title : String
63 { title : String
69− , items : List { icon : String, name : String, trigger : msg }
64+ , items : List { icon : String, name : String, trigger : msg, current : Bool }
70− , close : msg
65+ , toggle : msg
66+ , visible : Bool
67+ }
68+
69+
70+mapMenuConfig : (a -> b) -> MenuConfig a -> MenuConfig b
71+mapMenuConfig f { title, items, toggle, visible } =
72+ { items =
73+ List.map
74+ (\{ icon, name, trigger, current } ->
75+ { icon = icon, name = name, trigger = f trigger, current = current }
76+ )
77+ items
78+ , title = title
79+ , toggle = f toggle
80+ , visible = visible
71 }
81 }
72
82
73
83
74menu : MenuConfig msg -> Html msg
84menu : MenuConfig msg -> Html msg
75−menu { title, items, close } =
85+menu { title, items, toggle } =
76 div [ Attr.class "p-side-navigation--icons is-drawer-expanded" ]
86 div [ Attr.class "p-side-navigation--icons is-drawer-expanded" ]
77 [ div [ Attr.class "p-side-navigation__overlay" ] []
87 [ div [ Attr.class "p-side-navigation__overlay" ] []
78 , nav [ Attr.class "p-side-navigation__drawer" ]
88 , nav [ Attr.class "p-side-navigation__drawer" ]
79 [ div [ Attr.class "p-side-navigation__drawer-header" ]
89 [ div [ Attr.class "p-side-navigation__drawer-header" ]
80− [ a [ Attr.class "p-side-navigation__toggle--in-drawer", Ev.onClick close ] [ text title ]
90+ [ a [ Attr.class "p-side-navigation__toggle--in-drawer", Ev.onClick toggle ] [ text title ]
81 ]
91 ]
82 , ul [ Attr.class "p-side-navigation__list" ]
92 , ul [ Attr.class "p-side-navigation__list" ]
83 (items
93 (items
84 |> List.map
94 |> List.map
85− (\{ icon, name, trigger } ->
95+ (\{ icon, name, trigger, current } ->
86 li [ Attr.class "p-side-navigation__item" ]
96 li [ Attr.class "p-side-navigation__item" ]
87 [ a [ Attr.class "p-side-navigation__link", Ev.onClick trigger ]
97 [ a [ Attr.class "p-side-navigation__link", Ev.onClick trigger ]
98+ [ i
88− [ i [ Attr.class "p-side-navigation__icon", Attr.class ("p-icon--" ++ icon) ] []
99+ [ Attr.class "p-side-navigation__icon"
100+ , Attr.class ("p-icon--" ++ icon)
101+ , Attr.classList [ ( "is-active", current ) ]
102+ ]
103+ []
89 , text name
104 , text name
90 ]
105 ]
91 ]
106 ]
@@ -110,41 +125,25 @@ popView stack =
110
125
111
126
112showNavigationView :
127showNavigationView :
113− { popStack : msg
114− , toggleMenu : msg
115− }
116− -> PageStack model
128+ PageStack model
117 ->
129 ->
118 (model
130 (model
119 ->
131 ->
120 { title : String
132 { title : String
121 , body : List (Html msg)
133 , body : List (Html msg)
122− , menuConfig : Maybe (MenuConfig msg)
134+ , topButton : Maybe (TopButton msg)
123− , menuPossible : Bool
124 }
135 }
125 )
136 )
126 -> Html msg
137 -> Html msg
127−showNavigationView { popStack, toggleMenu } stack render =
138+showNavigationView stack render =
128 let
139 let
129− { title, body, menuConfig, menuPossible } =
140+ { title, body, topButton } =
130 render (Nonempty.head stack)
141 render (Nonempty.head stack)
131 in
142 in
132 div []
143 div []
133− (maybeList menuConfig menu
134− ++ [ navigation
144+ (navigation
135− { topButton =
145+ { topButton = topButton
136− if menuPossible then
137− Just MenuButton
138−
139− else if Nonempty.length stack > 1 then
140− Just BackButton
141−
142− else
143− Nothing
144− , title = title
146+ , title = title
145− , pop = popStack
146− , toggleMenu = toggleMenu
147− }
147+ }
148− , main_ [] body
148+ ++ [ main_ [] body ]
149− ]
150 )
149 )