on master help sign in
wip
812de5eiko
backend/Bridge.purs+16−1
@@ -115,9 +115,24 @@ derive instance genericSub_LoadCipher :: Generic Sub_LoadCipher _
115derive instance eqSub_LoadCipher :: Eq Sub_LoadCipher
115derive instance eqSub_LoadCipher :: Eq Sub_LoadCipher
116derive instance ordSub_LoadCipher :: Ord Sub_LoadCipher
116derive instance ordSub_LoadCipher :: Ord Sub_LoadCipher
117
117
118+data Sub_LoadCiphers_cipherType =
119+ CardType
120+ | IdentityType
121+ | LoginType
122+ | NoteType
123+
124+instance encodeJsonSub_LoadCiphers_cipherType :: EncodeJson Sub_LoadCiphers_cipherType where
125+ encodeJson = genericEncodeAeson Argonaut.defaultOptions
126+instance decodeJsonSub_LoadCiphers_cipherType :: DecodeJson Sub_LoadCiphers_cipherType where
127+ decodeJson = genericDecodeAeson Argonaut.defaultOptions
128+derive instance genericSub_LoadCiphers_cipherType :: Generic Sub_LoadCiphers_cipherType _
129+derive instance eqSub_LoadCiphers_cipherType :: Eq Sub_LoadCiphers_cipherType
130+derive instance ordSub_LoadCiphers_cipherType :: Ord Sub_LoadCiphers_cipherType
131+
118newtype Sub_LoadCiphers =
132newtype Sub_LoadCiphers =
119 Sub_LoadCiphers {
133 Sub_LoadCiphers {
134+ cipherType :: Sub_LoadCiphers_cipherType
120− date :: String
135+ , date :: String
121 , id :: String
136 , id :: String
122 , name :: String
137 , name :: String
123 }
138 }
backend/Main.purs+13−2
@@ -8,11 +8,12 @@ import BW (ApiService, CryptoService, Hash, Services)
8import BW as WB
8import BW as WB
9import BW.Logic (decrypt, hashPassword, liftPromise)
9import BW.Logic (decrypt, hashPassword, liftPromise)
10import BW.Logic as Logic
10import BW.Logic as Logic
11−import BW.Types (CipherResponse, Email(..), IdentityTokenResponse, Password(..), PreloginResponse, SyncResponse, Urls, cipherTypeCard, cipherTypeLogin, cipherTypeSecureNote)
11+import BW.Types (CipherResponse, Email(..), IdentityTokenResponse, Password(..), PreloginResponse, SyncResponse, Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
12import Bridge (Sub_LoadCipher(..))
12import Bridge (Sub_LoadCipher(..))
13import Bridge as Bridge
13import Bridge as Bridge
14import Control.El (class HasL, Al, L(..), l)
14import Control.El (class HasL, Al, L(..), l)
15import Control.Monad.Error.Class (throwError)
15import Control.Monad.Error.Class (throwError)
16+import Control.Monad.List.Trans as List
16import Control.Monad.Reader (runReaderT)
17import Control.Monad.Reader (runReaderT)
17import Data.Argonaut (class DecodeJson)
18import Data.Argonaut (class DecodeJson)
18import Data.Array as Array
19import Data.Array as Array
@@ -194,9 +195,19 @@ processCipher ::
194 CipherResponse -> Al r Bridge.Sub_LoadCiphers
195 CipherResponse -> Al r Bridge.Sub_LoadCiphers
195processCipher cipher = do
196processCipher cipher = do
196 name <- decrypt cipher.name
197 name <- decrypt cipher.name
198+ cipherType <- case cipher.type of
199+ n | n == cipherTypeLogin -> pure Bridge.LoginType
200+ n | n == cipherTypeSecureNote -> pure Bridge.NoteType
201+ n | n == cipherTypeCard -> pure Bridge.CardType
202+ n | n == cipherTypeIdentity -> pure Bridge.IdentityType
203+ n -> throwError $ error $ "Unsupported cipher type: " <> show n
197 pure
204 pure
198 $ Bridge.Sub_LoadCiphers
205 $ Bridge.Sub_LoadCiphers
206+ { name: name
199− { name: name, date: Timestamp.toLocalDateTimeString cipher.revisionDate, id: cipher.id }
207+ , date: Timestamp.toLocalDateTimeString cipher.revisionDate
208+ , id: cipher.id
209+ , cipherType
210+ }
200
211
201runElmAff :: FFI.Elm -> Aff Unit -> Effect Unit
212runElmAff :: FFI.Elm -> Aff Unit -> Effect Unit
202runElmAff app =
213runElmAff app =
bridge.yaml+5−1
@@ -6,6 +6,11 @@ Sub:
6 name: String
6 name: String
7 date: String
7 date: String
8 id: String
8 id: String
9+ cipherType:
10+ - LoginType
11+ - CardType
12+ - NoteType
13+ - IdentityType
9- LoginSuccessful
14- LoginSuccessful
10- NeedsMasterPassword:
15- NeedsMasterPassword:
11 server: String
16 server: String
@@ -28,7 +33,6 @@ Sub:
28 expYear: String
33 expYear: String
29 code: String
34 code: String
30
35
31−
32Cmd:
36Cmd:
33- Init
37- Init
34- Login:
38- Login:
frontend/Bridge.elm+27−3
@@ -179,15 +179,38 @@ jsonEncSub_LoadCipher val =
179
179
180
180
181
181
182+type Sub_LoadCiphers_cipherType =
183+ CardType
184+ | IdentityType
185+ | LoginType
186+ | NoteType
187+
188+jsonDecSub_LoadCiphers_cipherType : Json.Decode.Decoder ( Sub_LoadCiphers_cipherType )
189+jsonDecSub_LoadCiphers_cipherType =
190+ let jsonDecDictSub_LoadCiphers_cipherType = Dict.fromList [("CardType", CardType), ("IdentityType", IdentityType), ("LoginType", LoginType), ("NoteType", NoteType)]
191+ in decodeSumUnaries "Sub_LoadCiphers_cipherType" jsonDecDictSub_LoadCiphers_cipherType
192+
193+jsonEncSub_LoadCiphers_cipherType : Sub_LoadCiphers_cipherType -> Value
194+jsonEncSub_LoadCiphers_cipherType val =
195+ case val of
196+ CardType -> Json.Encode.string "CardType"
197+ IdentityType -> Json.Encode.string "IdentityType"
198+ LoginType -> Json.Encode.string "LoginType"
199+ NoteType -> Json.Encode.string "NoteType"
200+
201+
202+
182type alias Sub_LoadCiphers =
203type alias Sub_LoadCiphers =
204+ { cipherType: Sub_LoadCiphers_cipherType
183− { date: String
205+ , date: String
184 , id: String
206 , id: String
185 , name: String
207 , name: String
186 }
208 }
187
209
188jsonDecSub_LoadCiphers : Json.Decode.Decoder ( Sub_LoadCiphers )
210jsonDecSub_LoadCiphers : Json.Decode.Decoder ( Sub_LoadCiphers )
189jsonDecSub_LoadCiphers =
211jsonDecSub_LoadCiphers =
190− Json.Decode.succeed (\pdate pid pname -> {date = pdate, id = pid, name = pname})
212+ Json.Decode.succeed (\pcipherType pdate pid pname -> {cipherType = pcipherType, date = pdate, id = pid, name = pname})
213+ |> required "cipherType" (jsonDecSub_LoadCiphers_cipherType)
191 |> required "date" (Json.Decode.string)
214 |> required "date" (Json.Decode.string)
192 |> required "id" (Json.Decode.string)
215 |> required "id" (Json.Decode.string)
193 |> required "name" (Json.Decode.string)
216 |> required "name" (Json.Decode.string)
@@ -195,7 +218,8 @@ jsonDecSub_LoadCiphers =
195jsonEncSub_LoadCiphers : Sub_LoadCiphers -> Value
218jsonEncSub_LoadCiphers : Sub_LoadCiphers -> Value
196jsonEncSub_LoadCiphers val =
219jsonEncSub_LoadCiphers val =
197 Json.Encode.object
220 Json.Encode.object
221+ [ ("cipherType", jsonEncSub_LoadCiphers_cipherType val.cipherType)
198− [ ("date", Json.Encode.string val.date)
222+ , ("date", Json.Encode.string val.date)
199 , ("id", Json.Encode.string val.id)
223 , ("id", Json.Encode.string val.id)
200 , ("name", Json.Encode.string val.name)
224 , ("name", Json.Encode.string val.name)
201 ]
225 ]
frontend/Main.elm+63−5
@@ -34,6 +34,8 @@ 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
37
39
38
40
39type alias CipherId =
41type alias CipherId =
@@ -43,6 +45,9 @@ type alias CipherId =
43type alias Model =
45type alias Model =
44 { notifications : List Notification.Config
46 { notifications : List Notification.Config
45 , pageStack : Navigation.PageStack PageModel
47 , pageStack : Navigation.PageStack PageModel
48+ , menuShown : Bool
49+ , email : Maybe String
50+ , filter : CipherFilter
46 }
51 }
47
52
48
53
@@ -132,27 +137,77 @@ subscriptions _ =
132 )
137 )
133
138
134
139
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+
135init : ( Model, Cmd Msg )
155init : ( Model, Cmd Msg )
136init =
156init =
137 ( { notifications = []
157 ( { notifications = []
138 , pageStack = Nonempty.singleton LoadingPage
158 , pageStack = Nonempty.singleton LoadingPage
159+ , menuShown = False
160+ , email = Nothing
161+ , filter = AnyCipher
139 }
162 }
140 , FFI.sendBridge Bridge.Init
163 , FFI.sendBridge Bridge.Init
141 )
164 )
142
165
143
166
167+pageShouldShowMenu : PageModel -> Bool
168+pageShouldShowMenu page =
169+ case page of
170+ CiphersModel _ ->
171+ True
172+
173+ _ ->
174+ False
175+
176+
144view : Model -> Html Msg
177view : Model -> Html Msg
145view model =
178view model =
146− Navigation.showNavigationView { popStack = PopView }
179+ Navigation.showNavigationView
180+ { popStack = PopView
181+ , toggleMenu = ToggleMenu
182+ }
147 model.pageStack
183 model.pageStack
148 (\page ->
184 (\page ->
149 let
185 let
150 ( title, body ) =
186 ( title, body ) =
187+ case page of
188+ CiphersModel m ->
189+ CiphersModel { m | ciphers = m.ciphers |> applyCipherFilter model.filter }
190+ |> showPage
191+
192+ _ ->
151− showPage page
193+ showPage page
152 in
194 in
153− ( title
195+ { title = title
154− , maybeList (List.head model.notifications) (Notification.notification CloseNotification) ++ body
196+ , body = maybeList (List.head model.notifications) (Notification.notification CloseNotification) ++ body
155− )
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+ }
156 )
211 )
157
212
158
213
@@ -302,6 +357,9 @@ update msg model =
302 , Cmd.none
357 , Cmd.none
303 )
358 )
304
359
360+ ToggleMenu ->
361+ ( { model | menuShown = not model.menuShown }, Cmd.none )
362+
305
363
306loginCallbacks : Login.Callbacks Msg
364loginCallbacks : Login.Callbacks Msg
307loginCallbacks =
365loginCallbacks =
frontend/Pages/Navigation.elm+69−10
@@ -14,20 +14,50 @@ import List.Nonempty as Nonempty exposing (Nonempty)
14import Utils exposing (..)
14import Utils exposing (..)
15
15
16
16
17+type TopButton
18+ = BackButton
19+ | MenuButton
20+
21+
17type alias Config msg =
22type alias Config msg =
18− { back : Bool
23+ { topButton : Maybe TopButton
19 , title : String
24 , title : String
20 , pop : msg
25 , pop : msg
26+ , toggleMenu : msg
21 }
27 }
22
28
23
29
24navigation : Config msg -> Html msg
30navigation : Config msg -> Html msg
25−navigation { back, title, pop } =
31+navigation { topButton, title, pop, toggleMenu } =
26 nav [ Attr.class "p-tabs p-tabs__list" ]
32 nav [ Attr.class "p-tabs p-tabs__list" ]
27 [ h3 [ Attr.class "u-no-margin u-no-padding" ]
33 [ h3 [ Attr.class "u-no-margin u-no-padding" ]
28 [ button
34 [ button
29− (Attr.class "p-button--base is-inline u-no-margin" :: optional (not back) (Attr.style "visibility" "hidden"))
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
39+ Just BackButton ->
30− [ i [ Attr.class "p-icon--chevron-up ninety-counter", Ev.onClick pop ] []
40+ "p-icon--chevron-up ninety-counter"
41+
42+ Just MenuButton ->
43+ "p-icon--menu"
44+
45+ Nothing ->
46+ ""
47+ )
48+ , Ev.onClick
49+ (case topButton of
50+ Nothing ->
51+ pop
52+
53+ Just BackButton ->
54+ pop
55+
56+ Just MenuButton ->
57+ toggleMenu
58+ )
59+ ]
60+ []
31 ]
61 ]
32 , text title
62 , text title
33 ]
63 ]
@@ -79,13 +109,42 @@ popView stack =
79 Nonempty.tail stack |> Nonempty.fromList |> Maybe.withDefault stack
109 Nonempty.tail stack |> Nonempty.fromList |> Maybe.withDefault stack
80
110
81
111
82−showNavigationView : { popStack : msg } -> PageStack model -> (model -> ( String, List (Html msg) )) -> Html msg
112+showNavigationView :
113+ { popStack : msg
114+ , toggleMenu : msg
115+ }
116+ -> PageStack model
117+ ->
118+ (model
119+ ->
120+ { title : String
121+ , body : List (Html msg)
122+ , menuConfig : Maybe (MenuConfig msg)
123+ , menuPossible : Bool
124+ }
125+ )
126+ -> Html msg
83−showNavigationView { popStack } stack render =
127+showNavigationView { popStack, toggleMenu } stack render =
84 let
128 let
85− ( title, body ) =
129+ { title, body, menuConfig, menuPossible } =
86 render (Nonempty.head stack)
130 render (Nonempty.head stack)
87 in
131 in
88 div []
132 div []
133+ (maybeList menuConfig menu
134+ ++ [ navigation
135+ { topButton =
136+ if menuPossible then
137+ Just MenuButton
138+
89− [ navigation { back = Nonempty.length stack > 1, title = title, pop = popStack }
139+ else if Nonempty.length stack > 1 then
140+ Just BackButton
141+
142+ else
143+ Nothing
144+ , title = title
145+ , pop = popStack
146+ , toggleMenu = toggleMenu
147+ }
90− , main_ [] body
148+ , main_ [] body
91− ]
149+ ]
150+ )