Compare commits
3 Commits
3b370138aa
...
8181ef2dfa
Author | SHA1 | Date |
---|---|---|
Bram | 8181ef2dfa | |
Bram | 7711ce8c0d | |
Bram | 85790d4e7b |
2
elm.json
2
elm.json
|
@ -14,6 +14,7 @@
|
||||||
"icidasset/elm-material-icons": "11.0.0",
|
"icidasset/elm-material-icons": "11.0.0",
|
||||||
"kudzu-forest/elm-constant-time-queue": "1.4.0",
|
"kudzu-forest/elm-constant-time-queue": "1.4.0",
|
||||||
"mdgriffith/elm-ui": "1.1.8",
|
"mdgriffith/elm-ui": "1.1.8",
|
||||||
|
"micahhahn/elm-safe-recursion": "2.0.0",
|
||||||
"noordstar/elm-iddict": "1.0.1",
|
"noordstar/elm-iddict": "1.0.1",
|
||||||
"noordstar/elm-matrix-sdk-beta": "3.6.0",
|
"noordstar/elm-matrix-sdk-beta": "3.6.0",
|
||||||
"noordstar/elm-palette": "1.0.0"
|
"noordstar/elm-palette": "1.0.0"
|
||||||
|
@ -31,7 +32,6 @@
|
||||||
"elm/virtual-dom": "1.0.3",
|
"elm/virtual-dom": "1.0.3",
|
||||||
"elm-community/intdict": "3.1.0",
|
"elm-community/intdict": "3.1.0",
|
||||||
"fredcy/elm-parseint": "2.0.1",
|
"fredcy/elm-parseint": "2.0.1",
|
||||||
"micahhahn/elm-safe-recursion": "2.0.0",
|
|
||||||
"miniBill/elm-fast-dict": "1.2.1",
|
"miniBill/elm-fast-dict": "1.2.1",
|
||||||
"noahzgordon/elm-color-extra": "1.0.2",
|
"noahzgordon/elm-color-extra": "1.0.2",
|
||||||
"turboMaCk/queue": "1.1.0"
|
"turboMaCk/queue": "1.1.0"
|
||||||
|
|
|
@ -1,43 +1,58 @@
|
||||||
module Items.FlavorPicker exposing (..)
|
module Items.FlavorPicker exposing (..)
|
||||||
|
|
||||||
{-| This module allows the user to pick whatever flavor they want to use.
|
{-| This module allows the user to pick whatever flavor they want to use.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Color exposing (Color)
|
import Color exposing (Color)
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
|
import Element.Events
|
||||||
import Html.Attributes
|
import Html.Attributes
|
||||||
|
import Layout
|
||||||
import Material.Icons
|
import Material.Icons
|
||||||
import Theme exposing (Flavor(..))
|
import Theme exposing (Flavor(..))
|
||||||
import Layout
|
|
||||||
import Element.Events
|
|
||||||
|
|
||||||
-- MODEL
|
-- MODEL
|
||||||
|
|
||||||
-- UPDATE
|
-- UPDATE
|
||||||
|
|
||||||
-- VIEW
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
view :
|
view :
|
||||||
{ height : Int
|
{ height : Int
|
||||||
, flavor : Flavor
|
, flavor : Flavor
|
||||||
, onClick : Flavor -> msg
|
, onClick : Flavor -> msg
|
||||||
, themeIcon : Flavor -> Color
|
, themeIcon : Flavor -> Color
|
||||||
, width : Int
|
, width : Int
|
||||||
} -> Element msg
|
}
|
||||||
|
-> Element msg
|
||||||
view data =
|
view data =
|
||||||
let
|
let
|
||||||
lightMode = data.flavor == Latte
|
lightMode =
|
||||||
icon = if lightMode then Material.Icons.dark_mode else Material.Icons.light_mode
|
data.flavor == Latte
|
||||||
newFlavor = if lightMode then Frappe else Latte
|
|
||||||
in
|
|
||||||
Element.el
|
|
||||||
[ Element.Events.onClick (data.onClick newFlavor)
|
|
||||||
, Element.htmlAttribute (Html.Attributes.style "cursor" "pointer")
|
|
||||||
]
|
|
||||||
( Layout.iconAsElement
|
|
||||||
{ color = data.themeIcon data.flavor
|
|
||||||
, height = data.height
|
|
||||||
, icon = icon
|
|
||||||
, width = data.width
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
icon =
|
||||||
|
if lightMode then
|
||||||
|
Material.Icons.dark_mode
|
||||||
|
|
||||||
|
else
|
||||||
|
Material.Icons.light_mode
|
||||||
|
|
||||||
|
newFlavor =
|
||||||
|
if lightMode then
|
||||||
|
Frappe
|
||||||
|
|
||||||
|
else
|
||||||
|
Latte
|
||||||
|
in
|
||||||
|
Element.el
|
||||||
|
[ Element.Events.onClick (data.onClick newFlavor)
|
||||||
|
, Element.htmlAttribute (Html.Attributes.style "cursor" "pointer")
|
||||||
|
]
|
||||||
|
(Layout.iconAsElement
|
||||||
|
{ color = data.themeIcon data.flavor
|
||||||
|
, height = data.height
|
||||||
|
, icon = icon
|
||||||
|
, width = data.width
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -1,32 +1,40 @@
|
||||||
module Items.Introduction exposing (..)
|
module Items.Introduction exposing (..)
|
||||||
|
|
||||||
|
|
||||||
import Color exposing (Color)
|
import Color exposing (Color)
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
import Element.Background
|
import Element.Background
|
||||||
import Widget.Material.Typography
|
import Layout
|
||||||
import Theme
|
import Theme
|
||||||
|
import Widget.Material.Typography
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- MODEL
|
-- MODEL
|
||||||
|
|
||||||
type alias Model = ()
|
|
||||||
|
|
||||||
type alias Msg = ()
|
type alias Model =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias Msg =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- UPDATE
|
-- UPDATE
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
-- SUBSCRIPTIONS
|
||||||
|
|
||||||
-- VIEW
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
view :
|
view :
|
||||||
{ colorBackground : Color
|
{ colorBackground : Color
|
||||||
, width : Int
|
, width : Int
|
||||||
} -> Element msg
|
}
|
||||||
|
-> Element msg
|
||||||
view data =
|
view data =
|
||||||
[ header "Martiplier"
|
[ Layout.header "Martiplier"
|
||||||
, text "Martiplier (short for Matrix Plier) is a unique client. It doesn't let you browse rooms and have chat conversations."
|
, Layout.stdText "Martiplier (short for Matrix Plier) is a unique client. It doesn't let you browse rooms and have chat conversations."
|
||||||
, text "Instead, it offers you a more debug-like display of a user account. This helps when trying to do bulk operations, or to discover information about a client."
|
, Layout.stdText "Instead, it offers you a more debug-like display of a user account. This helps when trying to do bulk operations, or to discover information about a client."
|
||||||
]
|
]
|
||||||
|> Element.column
|
|> Element.column
|
||||||
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
|
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
|
||||||
|
@ -34,12 +42,3 @@ view data =
|
||||||
, Element.spacing 20
|
, Element.spacing 20
|
||||||
, Element.width (Element.px data.width)
|
, Element.width (Element.px data.width)
|
||||||
]
|
]
|
||||||
|
|
||||||
header : String -> Element msg
|
|
||||||
header =
|
|
||||||
Element.text >> Element.el Widget.Material.Typography.h1
|
|
||||||
>> List.singleton >> Element.paragraph []
|
|
||||||
|
|
||||||
text : String -> Element msg
|
|
||||||
text =
|
|
||||||
Element.text >> List.singleton >> Element.paragraph []
|
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
module Items.ItemPicker exposing (..)
|
||||||
|
|
||||||
|
import Color exposing (Color)
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Iddict exposing (Iddict)
|
||||||
|
import Layout
|
||||||
|
import Material.Icons
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model a =
|
||||||
|
{ hover : Maybe Int
|
||||||
|
, items : Iddict a
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= OnHover Int
|
||||||
|
| OnHoverOut Int
|
||||||
|
|
||||||
|
|
||||||
|
init : Iddict a -> Model a
|
||||||
|
init iddict =
|
||||||
|
{ hover = Nothing, items = iddict }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model a -> Model a
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
OnHover i ->
|
||||||
|
{ model | hover = Just i }
|
||||||
|
|
||||||
|
OnHoverOut i ->
|
||||||
|
if model.hover == Just i then
|
||||||
|
{ model | hover = Nothing }
|
||||||
|
|
||||||
|
else
|
||||||
|
model
|
||||||
|
|
||||||
|
|
||||||
|
updateContent : (Iddict a -> Iddict a) -> Model a -> Model a
|
||||||
|
updateContent f model =
|
||||||
|
{ model | items = f model.items }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
{-| Extract the original data set out of the item picker.
|
||||||
|
-}
|
||||||
|
extract : Model a -> Iddict a
|
||||||
|
extract =
|
||||||
|
.items
|
||||||
|
|
||||||
|
|
||||||
|
{-| Display the item picker. Note that the item should not be wider than 360px.
|
||||||
|
-}
|
||||||
|
view :
|
||||||
|
{ colorMenu : Color
|
||||||
|
, colorText : Color
|
||||||
|
, height : Int
|
||||||
|
, model : Model a
|
||||||
|
, toText : Int -> a -> String
|
||||||
|
, toTitle : Int -> a -> String
|
||||||
|
, onAddNew : Maybe msg
|
||||||
|
, onClick : Int -> msg
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
view data =
|
||||||
|
Layout.sideList
|
||||||
|
{ color = data.colorMenu
|
||||||
|
, items =
|
||||||
|
data.model.items
|
||||||
|
|> Iddict.toList
|
||||||
|
|> List.map
|
||||||
|
(\( iid, item ) ->
|
||||||
|
Layout.itemWithSubtext
|
||||||
|
{ color = data.colorText
|
||||||
|
, leftIcon = always Element.none
|
||||||
|
, onPress = Just (data.onClick iid)
|
||||||
|
, rightIcon = Layout.iconAsIcon Material.Icons.launch
|
||||||
|
, text = data.toText iid item
|
||||||
|
, title = data.toTitle iid item
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> (\items ->
|
||||||
|
case data.onAddNew of
|
||||||
|
Nothing ->
|
||||||
|
items
|
||||||
|
|
||||||
|
Just onAddNew ->
|
||||||
|
List.append items
|
||||||
|
[ Layout.itemWithSubtext
|
||||||
|
{ color = data.colorText
|
||||||
|
, leftIcon = Layout.iconAsIcon Material.Icons.add_circle
|
||||||
|
, onPress = Just onAddNew
|
||||||
|
, rightIcon = always Element.none
|
||||||
|
, text = "Click here"
|
||||||
|
, title = "Add new"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
, width = data.width
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
module Items.LogViewer exposing (..)
|
||||||
|
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Element.Font
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
-- UPDATE
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
viewRecent :
|
||||||
|
{ height : Int
|
||||||
|
, logs : List { channel : String, content : String }
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
viewRecent data =
|
||||||
|
let
|
||||||
|
channelWidth =
|
||||||
|
90
|
||||||
|
|
||||||
|
contentWidth =
|
||||||
|
data.width - channelWidth
|
||||||
|
in
|
||||||
|
Element.table
|
||||||
|
[]
|
||||||
|
{ data = data.logs
|
||||||
|
, columns =
|
||||||
|
[ { header = Element.el [ Element.Font.bold ] (Element.text "Channel")
|
||||||
|
, width = Element.px channelWidth
|
||||||
|
, view = .channel >> Element.text
|
||||||
|
}
|
||||||
|
, { header = Element.el [ Element.Font.bold ] (Element.text "Content")
|
||||||
|
, width = Element.px contentWidth
|
||||||
|
, view = .content >> String.replace "\n" " " >> stripText (contentWidth // 10) >> Element.text
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stripText : Int -> String -> String
|
||||||
|
stripText n text =
|
||||||
|
if String.length text < n then
|
||||||
|
text
|
||||||
|
|
||||||
|
else
|
||||||
|
String.left (n - 3) text ++ "..."
|
|
@ -1,4 +1,5 @@
|
||||||
module Items.LoginScreen exposing (..)
|
module Items.LoginView exposing (..)
|
||||||
|
|
||||||
{-| The Login screen allows the user to log in, as well as view a short display
|
{-| The Login screen allows the user to log in, as well as view a short display
|
||||||
of what to expect from the Matrix client.
|
of what to expect from the Matrix client.
|
||||||
-}
|
-}
|
||||||
|
@ -7,15 +8,18 @@ import Color exposing (Color)
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
import Element.Background
|
import Element.Background
|
||||||
import Element.Border
|
import Element.Border
|
||||||
|
import Items.FlavorPicker as FlavorPicker
|
||||||
|
import Layout
|
||||||
|
import Material.Icons
|
||||||
import Matrix
|
import Matrix
|
||||||
import Matrix.Settings
|
import Matrix.Settings
|
||||||
import Theme
|
import Theme
|
||||||
import Layout
|
|
||||||
import Items.FlavorPicker as FlavorPicker
|
|
||||||
import Material.Icons
|
|
||||||
|
|
||||||
-- MODEL
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ accessToken : String
|
{ accessToken : String
|
||||||
, loginMethod : LoginMethod
|
, loginMethod : LoginMethod
|
||||||
|
@ -23,16 +27,19 @@ type alias Model =
|
||||||
, username : String
|
, username : String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= SetAccessToken String
|
= SetAccessToken String
|
||||||
| SetPassword String
|
| SetPassword String
|
||||||
| SetUsername String
|
| SetUsername String
|
||||||
| SwitchMethod LoginMethod
|
| SwitchMethod LoginMethod
|
||||||
|
|
||||||
|
|
||||||
type LoginMethod
|
type LoginMethod
|
||||||
= AccessToken
|
= AccessToken
|
||||||
| Password
|
| Password
|
||||||
|
|
||||||
|
|
||||||
init : Model
|
init : Model
|
||||||
init =
|
init =
|
||||||
{ accessToken = ""
|
{ accessToken = ""
|
||||||
|
@ -41,8 +48,11 @@ init =
|
||||||
, username = ""
|
, username = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- UPDATE
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
update : Msg -> Model -> Model
|
update : Msg -> Model -> Model
|
||||||
update msg model =
|
update msg model =
|
||||||
case msg of
|
case msg of
|
||||||
|
@ -58,8 +68,11 @@ update msg model =
|
||||||
SwitchMethod method ->
|
SwitchMethod method ->
|
||||||
{ model | loginMethod = method }
|
{ model | loginMethod = method }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- VIEW
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
view :
|
view :
|
||||||
{ colorBackground : Color
|
{ colorBackground : Color
|
||||||
, colorMain : Color
|
, colorMain : Color
|
||||||
|
@ -73,7 +86,8 @@ view :
|
||||||
, onSubmit : Matrix.Vault -> msg
|
, onSubmit : Matrix.Vault -> msg
|
||||||
, toMsg : Msg -> msg
|
, toMsg : Msg -> msg
|
||||||
, width : Int
|
, width : Int
|
||||||
} -> Element msg
|
}
|
||||||
|
-> Element msg
|
||||||
view data =
|
view data =
|
||||||
[ viewLoginMethodPicker
|
[ viewLoginMethodPicker
|
||||||
{ color = data.colorMain
|
{ color = data.colorMain
|
||||||
|
@ -97,7 +111,7 @@ view data =
|
||||||
, show = False
|
, show = False
|
||||||
, text = data.model.accessToken
|
, text = data.model.accessToken
|
||||||
}
|
}
|
||||||
|
|
||||||
Password ->
|
Password ->
|
||||||
Layout.passwordInput
|
Layout.passwordInput
|
||||||
{ color = data.colorTextField
|
{ color = data.colorTextField
|
||||||
|
@ -108,17 +122,17 @@ view data =
|
||||||
, text = data.model.password
|
, text = data.model.password
|
||||||
}
|
}
|
||||||
, Layout.containedButton
|
, Layout.containedButton
|
||||||
{ buttonColor = data.colorMain
|
{ buttonColor = data.colorMain
|
||||||
, clickColor = data.colorText
|
, clickColor = data.colorText
|
||||||
, icon = always Element.none
|
, icon = always Element.none
|
||||||
, onPress =
|
, onPress =
|
||||||
data.model
|
data.model
|
||||||
|> toVault
|
|> toVault
|
||||||
|> Maybe.map data.onSubmit
|
|> Maybe.map data.onSubmit
|
||||||
, text = "LOG IN"
|
, text = "LOG IN"
|
||||||
}
|
}
|
||||||
|> Element.el
|
|> Element.el
|
||||||
[ Element.centerX ]
|
[ Element.centerX ]
|
||||||
]
|
]
|
||||||
|> Element.column
|
|> Element.column
|
||||||
[ Element.Background.color (Theme.toElmUiColor data.colorMenu)
|
[ Element.Background.color (Theme.toElmUiColor data.colorMenu)
|
||||||
|
@ -133,7 +147,7 @@ view data =
|
||||||
|> Element.el
|
|> Element.el
|
||||||
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
|
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
|
||||||
, Element.inFront
|
, Element.inFront
|
||||||
( FlavorPicker.view
|
(FlavorPicker.view
|
||||||
{ height = 30
|
{ height = 30
|
||||||
, flavor = data.flavor
|
, flavor = data.flavor
|
||||||
, onClick = data.onFlavorPick
|
, onClick = data.onFlavorPick
|
||||||
|
@ -147,6 +161,7 @@ view data =
|
||||||
, Element.width (Element.px data.width)
|
, Element.width (Element.px data.width)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
toVault : Model -> Maybe Matrix.Vault
|
toVault : Model -> Maybe Matrix.Vault
|
||||||
toVault model =
|
toVault model =
|
||||||
case model.loginMethod of
|
case model.loginMethod of
|
||||||
|
@ -168,6 +183,7 @@ toVault model =
|
||||||
|> Matrix.fromUserId
|
|> Matrix.fromUserId
|
||||||
|> Maybe.map (Matrix.Settings.setPassword model.password)
|
|> Maybe.map (Matrix.Settings.setPassword model.password)
|
||||||
|
|
||||||
|
|
||||||
viewLoginMethodPicker : { color : Color, loginMethod : LoginMethod, toMsg : LoginMethod -> msg } -> Element msg
|
viewLoginMethodPicker : { color : Color, loginMethod : LoginMethod, toMsg : LoginMethod -> msg } -> Element msg
|
||||||
viewLoginMethodPicker data =
|
viewLoginMethodPicker data =
|
||||||
Layout.tab
|
Layout.tab
|
||||||
|
@ -175,24 +191,24 @@ viewLoginMethodPicker data =
|
||||||
, content = always Element.none
|
, content = always Element.none
|
||||||
, items =
|
, items =
|
||||||
[ { icon = Layout.iconAsIcon Material.Icons.key
|
[ { icon = Layout.iconAsIcon Material.Icons.key
|
||||||
, text = "Access token"
|
, text = "Access token"
|
||||||
}
|
}
|
||||||
, { icon = Layout.iconAsIcon Material.Icons.password
|
, { icon = Layout.iconAsIcon Material.Icons.password
|
||||||
, text = "Password"
|
, text = "Password"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
, onSelect =
|
, onSelect =
|
||||||
(\i ->
|
\i ->
|
||||||
if i == 0 then
|
if i == 0 then
|
||||||
data.toMsg AccessToken
|
data.toMsg AccessToken
|
||||||
else
|
|
||||||
data.toMsg Password
|
else
|
||||||
)
|
data.toMsg Password
|
||||||
, selected =
|
, selected =
|
||||||
case data.loginMethod of
|
case data.loginMethod of
|
||||||
AccessToken ->
|
AccessToken ->
|
||||||
0
|
0
|
||||||
|
|
||||||
Password ->
|
Password ->
|
||||||
1
|
1
|
||||||
}
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
module Items.VaultList exposing (..)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
|
||||||
|
# Vault list
|
||||||
|
|
||||||
|
The vault list contains a list of stored vaults that can be picked from.
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Iddict exposing (Iddict)
|
||||||
|
import Matrix
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
Iddict VaultBlock
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= AddVault { name : String, vault : Matrix.Vault }
|
||||||
|
| OnVaultUpdate Int Matrix.VaultUpdate
|
||||||
|
|
||||||
|
|
||||||
|
type alias VaultBlock =
|
||||||
|
{ logs : List { channel : String, content : String }
|
||||||
|
, name : String
|
||||||
|
, vault : Matrix.Vault
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
Iddict.empty
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
addVault : { name : String, vault : Matrix.Vault } -> Model -> Model
|
||||||
|
addVault =
|
||||||
|
AddVault >> update
|
||||||
|
|
||||||
|
|
||||||
|
insertVaultUpdate : Int -> Matrix.VaultUpdate -> Model -> Model
|
||||||
|
insertVaultUpdate i vu =
|
||||||
|
update (OnVaultUpdate i vu)
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> Model
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
AddVault { name, vault } ->
|
||||||
|
Iddict.insert { name = name, logs = [], vault = vault } model
|
||||||
|
|> Tuple.second
|
||||||
|
|
||||||
|
OnVaultUpdate i vu ->
|
||||||
|
Iddict.update i
|
||||||
|
(Maybe.map
|
||||||
|
(\block ->
|
||||||
|
{ block
|
||||||
|
| logs =
|
||||||
|
List.append (Matrix.logs vu) block.logs
|
||||||
|
|> List.take 500
|
||||||
|
, vault = Matrix.update vu block.vault
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
model
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
getVault : Int -> Model -> Maybe Matrix.Vault
|
||||||
|
getVault i model =
|
||||||
|
Iddict.get i model |> Maybe.map .vault
|
|
@ -1,112 +0,0 @@
|
||||||
module Items.VaultPicker exposing (..)
|
|
||||||
|
|
||||||
import Color exposing (Color)
|
|
||||||
import Element exposing (Element)
|
|
||||||
import Iddict exposing (Iddict)
|
|
||||||
import Material.Icons
|
|
||||||
import Matrix
|
|
||||||
import Layout
|
|
||||||
|
|
||||||
-- MODEL
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ hover : Maybe Int, vaults : Iddict NamedVault }
|
|
||||||
|
|
||||||
type alias NamedVault = { name : String, vault : Matrix.Vault }
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= AddVault String Matrix.Vault
|
|
||||||
| OnHover Int
|
|
||||||
| OnHoverOut Int
|
|
||||||
| OnRemoveVault Int
|
|
||||||
|
|
||||||
init : List NamedVault -> Model
|
|
||||||
init items =
|
|
||||||
{ hover = Nothing
|
|
||||||
, vaults =
|
|
||||||
items
|
|
||||||
|> List.indexedMap Tuple.pair
|
|
||||||
|> Iddict.fromList
|
|
||||||
}
|
|
||||||
|
|
||||||
-- UPDATE
|
|
||||||
|
|
||||||
addVault : NamedVault -> Model -> Model
|
|
||||||
addVault data =
|
|
||||||
AddVault data.name data.vault |> update
|
|
||||||
|
|
||||||
update : Msg -> Model -> Model
|
|
||||||
update msg model =
|
|
||||||
case msg of
|
|
||||||
AddVault name vault ->
|
|
||||||
{ model
|
|
||||||
| vaults =
|
|
||||||
model.vaults
|
|
||||||
|> Iddict.insert { name = name, vault = vault }
|
|
||||||
|> Tuple.second
|
|
||||||
}
|
|
||||||
|
|
||||||
OnHover i ->
|
|
||||||
{ model | hover = Just i }
|
|
||||||
|
|
||||||
OnHoverOut i ->
|
|
||||||
case model.hover of
|
|
||||||
Just h ->
|
|
||||||
if i == h then
|
|
||||||
{ model | hover = Nothing }
|
|
||||||
else
|
|
||||||
model
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
model
|
|
||||||
|
|
||||||
OnRemoveVault i ->
|
|
||||||
{ model | vaults = Iddict.remove i model.vaults }
|
|
||||||
|
|
||||||
-- VIEW
|
|
||||||
|
|
||||||
getVault : Int -> Model -> Maybe Matrix.Vault
|
|
||||||
getVault i model =
|
|
||||||
Iddict.get i model.vaults |> Maybe.map .vault
|
|
||||||
|
|
||||||
view :
|
|
||||||
{ colorItem : Color
|
|
||||||
, colorList : Color
|
|
||||||
, model : Model
|
|
||||||
, onAddVault : msg
|
|
||||||
, onSelectVault : Int -> msg
|
|
||||||
, width : Int
|
|
||||||
} -> Element msg
|
|
||||||
view data =
|
|
||||||
Layout.sideList
|
|
||||||
{ color = data.colorList
|
|
||||||
, items =
|
|
||||||
data.model.vaults
|
|
||||||
|> Iddict.toList
|
|
||||||
|> List.map
|
|
||||||
(\(vid, { name, vault }) ->
|
|
||||||
Layout.itemWithSubtext
|
|
||||||
{ color = data.colorItem
|
|
||||||
, leftIcon = always Element.none
|
|
||||||
, onPress = Just (data.onSelectVault vid)
|
|
||||||
, rightIcon = Layout.iconAsIcon Material.Icons.launch
|
|
||||||
, text = name
|
|
||||||
, title = "Vault #" ++ String.fromInt vid
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> (\items ->
|
|
||||||
[ Layout.itemWithSubtext
|
|
||||||
{ color = data.colorItem
|
|
||||||
, leftIcon = Layout.iconAsIcon Material.Icons.add_circle
|
|
||||||
, onPress = Just data.onAddVault
|
|
||||||
, rightIcon = always Element.none
|
|
||||||
, text = "Click here"
|
|
||||||
, title = "Add new"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|> List.append items
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|> Element.el
|
|
||||||
[ Element.width (Element.px data.width) ]
|
|
|
@ -1,170 +0,0 @@
|
||||||
module Items.VaultScreen exposing (..)
|
|
||||||
|
|
||||||
import Color exposing (Color)
|
|
||||||
import Element exposing (Element)
|
|
||||||
import Layout
|
|
||||||
import Matrix
|
|
||||||
import Matrix.Room
|
|
||||||
import Queue exposing (Queue)
|
|
||||||
|
|
||||||
-- MODEL
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ recentLogs : Queue { channel : String, content : String }
|
|
||||||
, screen : Screen
|
|
||||||
}
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= OnSwitchScreen Screen
|
|
||||||
| OnVaultUpdate Matrix.VaultUpdate
|
|
||||||
|
|
||||||
type Screen
|
|
||||||
= LandingScreen
|
|
||||||
| Room String
|
|
||||||
|
|
||||||
init : Model
|
|
||||||
init =
|
|
||||||
{ recentLogs = Queue.empty
|
|
||||||
, screen = LandingScreen
|
|
||||||
}
|
|
||||||
|
|
||||||
maxItemsInRecentLogsQueue : Int
|
|
||||||
maxItemsInRecentLogsQueue = 100
|
|
||||||
|
|
||||||
-- UPDATE
|
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
|
||||||
update msg model =
|
|
||||||
case msg of
|
|
||||||
OnSwitchScreen screen ->
|
|
||||||
( { model | screen = screen }, Cmd.none )
|
|
||||||
|
|
||||||
OnVaultUpdate vu ->
|
|
||||||
( { model
|
|
||||||
| recentLogs =
|
|
||||||
model.recentLogs
|
|
||||||
|> addItemsToQueue (Matrix.logs vu)
|
|
||||||
|> stripQueueToSize maxItemsInRecentLogsQueue
|
|
||||||
}
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
addItemsToQueue : List a -> Queue a -> Queue a
|
|
||||||
addItemsToQueue items queue =
|
|
||||||
List.foldl Queue.enqueue queue items
|
|
||||||
|
|
||||||
stripQueueToSize : Int -> Queue a -> Queue a
|
|
||||||
stripQueueToSize i queue =
|
|
||||||
if Queue.length queue > i then
|
|
||||||
stripQueueToSize i (Queue.dequeue queue)
|
|
||||||
else
|
|
||||||
queue
|
|
||||||
|
|
||||||
-- VIEW
|
|
||||||
|
|
||||||
view :
|
|
||||||
{ colorSelectedRoom : Color
|
|
||||||
, colorText : Color
|
|
||||||
, height : Int
|
|
||||||
, model : Model
|
|
||||||
, onVaultUpdate : Matrix.VaultUpdate -> msg
|
|
||||||
, vault : Matrix.Vault
|
|
||||||
, toMsg : Msg -> msg
|
|
||||||
, width : Int
|
|
||||||
} -> Element msg
|
|
||||||
view data =
|
|
||||||
viewScreen
|
|
||||||
{ colorSelectedRoom = data.colorSelectedRoom
|
|
||||||
, colorText = data.colorText
|
|
||||||
, height = data.height
|
|
||||||
, model = data.model
|
|
||||||
, onVaultUpdate = data.onVaultUpdate
|
|
||||||
, vault = data.vault
|
|
||||||
, screen = data.model.screen
|
|
||||||
, toMsg = data.toMsg
|
|
||||||
, width = data.width
|
|
||||||
}
|
|
||||||
|
|
||||||
viewScreen :
|
|
||||||
{ colorSelectedRoom : Color
|
|
||||||
, colorText : Color
|
|
||||||
, height : Int
|
|
||||||
, model : Model
|
|
||||||
, onVaultUpdate : Matrix.VaultUpdate -> msg
|
|
||||||
, vault : Matrix.Vault
|
|
||||||
, screen : Screen
|
|
||||||
, toMsg : Msg -> msg
|
|
||||||
, width : Int
|
|
||||||
} -> Element msg
|
|
||||||
viewScreen data =
|
|
||||||
case data.screen of
|
|
||||||
LandingScreen ->
|
|
||||||
Element.wrappedRow
|
|
||||||
[ Element.height (Element.px data.height)
|
|
||||||
, Element.width (Element.px data.width)
|
|
||||||
]
|
|
||||||
[ viewRoomList
|
|
||||||
{ colorSelected = data.colorSelectedRoom
|
|
||||||
, colorText = data.colorText
|
|
||||||
, rooms = Matrix.rooms data.vault
|
|
||||||
, toMsg = data.toMsg
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Room roomId ->
|
|
||||||
case Matrix.fromRoomId roomId data.vault of
|
|
||||||
Just room ->
|
|
||||||
viewRoom
|
|
||||||
{ height = data.height
|
|
||||||
, model = room
|
|
||||||
, toMsg = data.toMsg
|
|
||||||
, width = data.width
|
|
||||||
}
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
viewScreen { data | screen = LandingScreen }
|
|
||||||
|
|
||||||
|
|
||||||
viewRoom :
|
|
||||||
{ height : Int
|
|
||||||
, model : Matrix.Room.Room
|
|
||||||
, toMsg : Msg -> msg
|
|
||||||
, width : Int
|
|
||||||
} -> Element msg
|
|
||||||
viewRoom data =
|
|
||||||
[ Matrix.Room.name data.model
|
|
||||||
|> Maybe.withDefault "Nameless room"
|
|
||||||
|> Element.text
|
|
||||||
]
|
|
||||||
|> Element.column []
|
|
||||||
|
|
||||||
viewRoomList :
|
|
||||||
{ colorSelected : Color
|
|
||||||
, colorText : Color
|
|
||||||
, rooms : List Matrix.Room.Room
|
|
||||||
, toMsg : Msg -> msg
|
|
||||||
} -> Element msg
|
|
||||||
viewRoomList data =
|
|
||||||
Layout.sideList
|
|
||||||
{ color = data.colorSelected
|
|
||||||
, items =
|
|
||||||
data.rooms
|
|
||||||
|> List.map
|
|
||||||
(\room ->
|
|
||||||
Layout.itemWithSubtext
|
|
||||||
{ color = data.colorText
|
|
||||||
, leftIcon = always Element.none -- TODO: Add room image
|
|
||||||
, onPress =
|
|
||||||
Matrix.Room.roomId room
|
|
||||||
|> Room
|
|
||||||
|> OnSwitchScreen
|
|
||||||
|> data.toMsg
|
|
||||||
|> Just
|
|
||||||
, rightIcon = always Element.none -- TODO: Choose icon?
|
|
||||||
, text = Matrix.Room.roomId room
|
|
||||||
, title =
|
|
||||||
Matrix.Room.name room
|
|
||||||
|> Maybe.withDefault "Nameless room"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,178 +0,0 @@
|
||||||
module Items.WelcomeScreen exposing (..)
|
|
||||||
|
|
||||||
import Color exposing (Color)
|
|
||||||
import Element exposing (Element)
|
|
||||||
import Element.Background
|
|
||||||
import Items.FlavorPicker as FlavorPicker
|
|
||||||
import Items.Introduction as Intro
|
|
||||||
import Items.LoginScreen as Login
|
|
||||||
import Items.VaultPicker as VaultPicker
|
|
||||||
import Matrix
|
|
||||||
import Theme
|
|
||||||
|
|
||||||
-- MODEL
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ loginView : Maybe Login.Model
|
|
||||||
, vaultView : VaultPicker.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= OnAddVault
|
|
||||||
| OnLoginView Login.Msg
|
|
||||||
| OnSubmitVault { name : String, vault : Matrix.Vault }
|
|
||||||
| OnVaultView VaultPicker.Msg
|
|
||||||
|
|
||||||
init : List { name : String, vault : Matrix.Vault } -> Model
|
|
||||||
init vaults =
|
|
||||||
{ loginView = Nothing
|
|
||||||
, vaultView = VaultPicker.init vaults
|
|
||||||
}
|
|
||||||
|
|
||||||
-- UPDATE
|
|
||||||
|
|
||||||
update : Msg -> Model -> Model
|
|
||||||
update msg model =
|
|
||||||
case msg of
|
|
||||||
OnAddVault ->
|
|
||||||
case model.loginView of
|
|
||||||
Just _ ->
|
|
||||||
model
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
{ model | loginView = Just Login.init }
|
|
||||||
|
|
||||||
OnLoginView m ->
|
|
||||||
{ model
|
|
||||||
| loginView = Maybe.map (Login.update m) model.loginView
|
|
||||||
}
|
|
||||||
|
|
||||||
OnSubmitVault vault ->
|
|
||||||
{ model
|
|
||||||
| loginView = Nothing
|
|
||||||
, vaultView = VaultPicker.addVault vault model.vaultView
|
|
||||||
}
|
|
||||||
|
|
||||||
OnVaultView m ->
|
|
||||||
{ model
|
|
||||||
| vaultView = VaultPicker.update m model.vaultView
|
|
||||||
}
|
|
||||||
|
|
||||||
-- VIEW
|
|
||||||
|
|
||||||
getVault : Int -> Model -> Maybe Matrix.Vault
|
|
||||||
getVault i model =
|
|
||||||
VaultPicker.getVault i model.vaultView
|
|
||||||
|
|
||||||
view :
|
|
||||||
{ colorBackground : Color
|
|
||||||
, colorBackground2 : Color
|
|
||||||
, colorMain : Color
|
|
||||||
, colorMenu : Color
|
|
||||||
, colorText : Color
|
|
||||||
, colorTextField : Color
|
|
||||||
, height : Int
|
|
||||||
, flavor : Theme.Flavor
|
|
||||||
, model : Model
|
|
||||||
, onFlavorPick : Theme.Flavor -> msg
|
|
||||||
, onSelectVault : Int -> msg
|
|
||||||
, toMsg : Msg -> msg
|
|
||||||
, width : Int
|
|
||||||
} -> Element msg
|
|
||||||
view data =
|
|
||||||
let
|
|
||||||
goesVertical = 2 * data.width <= 3 * data.height
|
|
||||||
direction = if goesVertical then Element.column else Element.row
|
|
||||||
width = if goesVertical then data.width else data.width // 2
|
|
||||||
height = if goesVertical then data.height // 2 else data.height
|
|
||||||
in
|
|
||||||
case data.model.loginView of
|
|
||||||
Just m ->
|
|
||||||
direction
|
|
||||||
[ Element.height (Element.px data.height)
|
|
||||||
, Element.width (Element.px data.width)
|
|
||||||
]
|
|
||||||
[ viewIntroduction
|
|
||||||
{ colorBackground = data.colorBackground
|
|
||||||
, colorItem = data.colorText
|
|
||||||
, colorList = data.colorMenu
|
|
||||||
, height = height
|
|
||||||
, model = data.model.vaultView
|
|
||||||
, onSelectVault = data.onSelectVault
|
|
||||||
, toMsg = data.toMsg
|
|
||||||
, width = width
|
|
||||||
}
|
|
||||||
, Login.view
|
|
||||||
{ colorBackground = data.colorBackground2
|
|
||||||
, colorMain = data.colorMain
|
|
||||||
, colorMenu = data.colorMenu
|
|
||||||
, colorText = data.colorText
|
|
||||||
, colorTextField = data.colorTextField
|
|
||||||
, height = height
|
|
||||||
, flavor = data.flavor
|
|
||||||
, model = m
|
|
||||||
, onFlavorPick = data.onFlavorPick
|
|
||||||
, onSubmit = \vault -> OnSubmitVault { name = "New Vault", vault = vault } |> data.toMsg
|
|
||||||
, toMsg = OnLoginView >> data.toMsg
|
|
||||||
, width = width
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
viewIntroduction
|
|
||||||
{ colorBackground = data.colorBackground
|
|
||||||
, colorItem = data.colorText
|
|
||||||
, colorList = data.colorMenu
|
|
||||||
, height = data.height
|
|
||||||
, model = data.model.vaultView
|
|
||||||
, onSelectVault = data.onSelectVault
|
|
||||||
, toMsg = data.toMsg
|
|
||||||
, width = data.width
|
|
||||||
}
|
|
||||||
|> Element.el
|
|
||||||
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
|
|
||||||
, Element.inFront
|
|
||||||
( FlavorPicker.view
|
|
||||||
{ height = 30
|
|
||||||
, flavor = data.flavor
|
|
||||||
, onClick = data.onFlavorPick
|
|
||||||
, themeIcon = always data.colorText
|
|
||||||
, width = 30
|
|
||||||
}
|
|
||||||
|> Element.el [ Element.alignRight ]
|
|
||||||
|> Element.el [ Element.padding 10, Element.width Element.fill ]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
viewIntroduction :
|
|
||||||
{ colorBackground : Color
|
|
||||||
, colorItem : Color
|
|
||||||
, colorList : Color
|
|
||||||
, height : Int
|
|
||||||
, model : VaultPicker.Model
|
|
||||||
, onSelectVault : Int -> msg
|
|
||||||
, toMsg : Msg -> msg
|
|
||||||
, width : Int
|
|
||||||
} -> Element msg
|
|
||||||
viewIntroduction data =
|
|
||||||
[ Intro.view
|
|
||||||
{ colorBackground = data.colorBackground
|
|
||||||
, width = data.width
|
|
||||||
}
|
|
||||||
, VaultPicker.view
|
|
||||||
{ colorItem = data.colorItem
|
|
||||||
, colorList = data.colorList
|
|
||||||
, onAddVault = data.toMsg OnAddVault
|
|
||||||
, onSelectVault = data.onSelectVault
|
|
||||||
, model = data.model
|
|
||||||
, width = Basics.min 360 (4 * data.width // 5)
|
|
||||||
}
|
|
||||||
|> Element.el
|
|
||||||
[ Element.centerX
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|> Element.column
|
|
||||||
[ Element.height (Element.px data.height)
|
|
||||||
, Element.scrollbarY
|
|
||||||
, Element.width (Element.px data.width)
|
|
||||||
]
|
|
174
src/Layout.elm
174
src/Layout.elm
|
@ -1,9 +1,13 @@
|
||||||
module Layout exposing
|
module Layout exposing
|
||||||
( tab
|
( twoBlocks
|
||||||
|
, tab, sideIconBar
|
||||||
, iconAsElement, iconAsIcon
|
, iconAsElement, iconAsIcon
|
||||||
, containedButton, outlinedButton, textButton, sideList
|
, containedButton, outlinedButton, textButton
|
||||||
, textInput, passwordInput
|
, textInput, passwordInput
|
||||||
, loadingIndicator, itemWithSubtext
|
, header, stdText
|
||||||
|
, itemWithSubtext
|
||||||
|
, sideList
|
||||||
|
, loadingIndicator
|
||||||
)
|
)
|
||||||
|
|
||||||
{-|
|
{-|
|
||||||
|
@ -15,14 +19,21 @@ The layout module exposes some boilerplate functions that have produce a
|
||||||
beautiful Material design Elm webpage.
|
beautiful Material design Elm webpage.
|
||||||
|
|
||||||
|
|
||||||
|
## Screen layout
|
||||||
|
|
||||||
|
@docs twoBlocks
|
||||||
|
|
||||||
|
|
||||||
## Elements
|
## Elements
|
||||||
|
|
||||||
@docs tab
|
@docs tab, sideIconBar
|
||||||
|
|
||||||
|
|
||||||
## Icons
|
## Icons
|
||||||
|
|
||||||
@docs iconAsElement, iconAsIcon
|
@docs iconAsElement, iconAsIcon
|
||||||
|
|
||||||
|
|
||||||
## Buttons
|
## Buttons
|
||||||
|
|
||||||
@docs containedButton, outlinedButton, textButton
|
@docs containedButton, outlinedButton, textButton
|
||||||
|
@ -32,14 +43,22 @@ beautiful Material design Elm webpage.
|
||||||
|
|
||||||
@docs textInput, passwordInput
|
@docs textInput, passwordInput
|
||||||
|
|
||||||
|
|
||||||
|
## Text
|
||||||
|
|
||||||
|
@docs header, stdText
|
||||||
|
|
||||||
|
|
||||||
## Items in a list
|
## Items in a list
|
||||||
|
|
||||||
@docs itemWithSubtext
|
@docs itemWithSubtext
|
||||||
|
|
||||||
|
|
||||||
## Lists
|
## Lists
|
||||||
|
|
||||||
@docs sideList
|
@docs sideList
|
||||||
|
|
||||||
|
|
||||||
## Other elements
|
## Other elements
|
||||||
|
|
||||||
@docs loadingIndicator
|
@docs loadingIndicator
|
||||||
|
@ -48,12 +67,18 @@ beautiful Material design Elm webpage.
|
||||||
|
|
||||||
import Color exposing (Color)
|
import Color exposing (Color)
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
|
import Element.Background
|
||||||
|
import Element.Events
|
||||||
|
import Element.Font
|
||||||
import Element.Input
|
import Element.Input
|
||||||
|
import Html.Attributes
|
||||||
|
import Material.Icons.Types
|
||||||
|
import Theme
|
||||||
import Widget
|
import Widget
|
||||||
import Widget.Customize as Customize
|
import Widget.Customize as Customize
|
||||||
import Widget.Icon exposing (Icon)
|
import Widget.Icon exposing (Icon)
|
||||||
import Widget.Material as Material
|
import Widget.Material as Material
|
||||||
import Material.Icons.Types
|
import Widget.Material.Typography
|
||||||
|
|
||||||
|
|
||||||
{-| A contained button representing the most important action of a group.
|
{-| A contained button representing the most important action of a group.
|
||||||
|
@ -75,12 +100,22 @@ containedButton data =
|
||||||
)
|
)
|
||||||
{ text = data.text, icon = data.icon, onPress = data.onPress }
|
{ text = data.text, icon = data.icon, onPress = data.onPress }
|
||||||
|
|
||||||
|
|
||||||
|
header : String -> Element msg
|
||||||
|
header =
|
||||||
|
Element.text
|
||||||
|
>> Element.el Widget.Material.Typography.h1
|
||||||
|
>> List.singleton
|
||||||
|
>> Element.paragraph []
|
||||||
|
|
||||||
|
|
||||||
iconAsElement :
|
iconAsElement :
|
||||||
{ color : Color
|
{ color : Color
|
||||||
, height : Int
|
, height : Int
|
||||||
, icon : Material.Icons.Types.Icon msg
|
, icon : Material.Icons.Types.Icon msg
|
||||||
, width : Int
|
, width : Int
|
||||||
} -> Element msg
|
}
|
||||||
|
-> Element msg
|
||||||
iconAsElement data =
|
iconAsElement data =
|
||||||
data.icon
|
data.icon
|
||||||
|> iconAsIcon
|
|> iconAsIcon
|
||||||
|
@ -91,10 +126,12 @@ iconAsElement data =
|
||||||
, Element.width (Element.px data.width)
|
, Element.width (Element.px data.width)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
iconAsIcon : Material.Icons.Types.Icon msg -> Widget.Icon.Icon msg
|
iconAsIcon : Material.Icons.Types.Icon msg -> Widget.Icon.Icon msg
|
||||||
iconAsIcon =
|
iconAsIcon =
|
||||||
Widget.Icon.elmMaterialIcons Material.Icons.Types.Color
|
Widget.Icon.elmMaterialIcons Material.Icons.Types.Color
|
||||||
|
|
||||||
|
|
||||||
{-| Multiline item
|
{-| Multiline item
|
||||||
-}
|
-}
|
||||||
itemWithSubtext :
|
itemWithSubtext :
|
||||||
|
@ -105,10 +142,10 @@ itemWithSubtext :
|
||||||
, text : String
|
, text : String
|
||||||
, title : String
|
, title : String
|
||||||
}
|
}
|
||||||
-> Widget.Item msg
|
-> Widget.Item msg
|
||||||
itemWithSubtext data =
|
itemWithSubtext data =
|
||||||
Widget.multiLineItem
|
Widget.multiLineItem
|
||||||
( { primary = data.color, onPrimary = data.color }
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|> singlePalette
|
|> singlePalette
|
||||||
|> Material.multiLineItem
|
|> Material.multiLineItem
|
||||||
)
|
)
|
||||||
|
@ -119,20 +156,22 @@ itemWithSubtext data =
|
||||||
, text = data.text
|
, text = data.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Circular loading bar indicator
|
{-| Circular loading bar indicator
|
||||||
-}
|
-}
|
||||||
loadingIndicator :
|
loadingIndicator :
|
||||||
{ color : Color
|
{ color : Color
|
||||||
}
|
}
|
||||||
-> Element msg
|
-> Element msg
|
||||||
loadingIndicator data =
|
loadingIndicator data =
|
||||||
Widget.circularProgressIndicator
|
Widget.circularProgressIndicator
|
||||||
( { primary = data.color, onPrimary = data.color }
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|> singlePalette
|
|> singlePalette
|
||||||
|> Material.progressIndicator
|
|> Material.progressIndicator
|
||||||
)
|
)
|
||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
|
|
||||||
{-| An outlined button representing an important action within a group.
|
{-| An outlined button representing an important action within a group.
|
||||||
-}
|
-}
|
||||||
outlinedButton :
|
outlinedButton :
|
||||||
|
@ -150,6 +189,7 @@ outlinedButton data =
|
||||||
)
|
)
|
||||||
{ text = data.text, icon = data.icon, onPress = data.onPress }
|
{ text = data.text, icon = data.icon, onPress = data.onPress }
|
||||||
|
|
||||||
|
|
||||||
{-| Show a password field
|
{-| Show a password field
|
||||||
-}
|
-}
|
||||||
passwordInput :
|
passwordInput :
|
||||||
|
@ -197,14 +237,79 @@ singlePalette { primary, onPrimary } =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sideList : { color : Color, items : List (Widget.Item msg) }-> Element msg
|
|
||||||
|
sideIconBar :
|
||||||
|
{ colorBackground : Color
|
||||||
|
, colorText : Color
|
||||||
|
, height : Int
|
||||||
|
, items : List { icon : Widget.Icon.Icon msg, onPress : msg, text : String }
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
sideIconBar data =
|
||||||
|
let
|
||||||
|
buttonHeight =
|
||||||
|
round (toFloat data.width * 1.618)
|
||||||
|
|
||||||
|
fontSize =
|
||||||
|
data.width // 6
|
||||||
|
|
||||||
|
iconSize =
|
||||||
|
data.width * 3 // 5
|
||||||
|
in
|
||||||
|
data.items
|
||||||
|
|> List.map
|
||||||
|
(\item ->
|
||||||
|
[ item.icon { size = iconSize, color = data.colorText }
|
||||||
|
|> Element.el [ Element.centerX ]
|
||||||
|
, Element.paragraph [] [ Element.text item.text ]
|
||||||
|
]
|
||||||
|
|> Element.column
|
||||||
|
[ Element.centerX
|
||||||
|
, Element.centerY
|
||||||
|
, Element.Font.bold
|
||||||
|
, Element.Font.center
|
||||||
|
, Element.Font.size fontSize
|
||||||
|
, Element.htmlAttribute (Html.Attributes.style "cursor" "pointer")
|
||||||
|
]
|
||||||
|
|> Element.el
|
||||||
|
[ Element.centerY
|
||||||
|
, Element.Events.onClick item.onPress
|
||||||
|
, Element.height (Element.px data.width)
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
||||||
|
|> Element.el
|
||||||
|
[ Element.height (Element.px buttonHeight)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> Element.column
|
||||||
|
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
|
||||||
|
, Element.height (Element.px data.height)
|
||||||
|
, Element.scrollbarY
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
sideList : { color : Color, items : List (Widget.Item msg), width : Int } -> Element msg
|
||||||
sideList data =
|
sideList data =
|
||||||
|
let
|
||||||
|
width px =
|
||||||
|
Element.width (Element.px px)
|
||||||
|
in
|
||||||
Widget.itemList
|
Widget.itemList
|
||||||
( { primary = data.color, onPrimary = data.color }
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|> singlePalette
|
|> singlePalette
|
||||||
|> Material.sideSheet
|
|> Material.sideSheet
|
||||||
)
|
)
|
||||||
data.items
|
data.items
|
||||||
|
|> Element.el [ Element.centerX, width (Basics.min 360 data.width) ]
|
||||||
|
|> Element.el [ width data.width ]
|
||||||
|
|
||||||
|
|
||||||
|
stdText : String -> Element msg
|
||||||
|
stdText =
|
||||||
|
Element.text >> List.singleton >> Element.paragraph []
|
||||||
|
|
||||||
|
|
||||||
{-| A tab selector that always has an item selected.
|
{-| A tab selector that always has an item selected.
|
||||||
-}
|
-}
|
||||||
|
@ -275,3 +380,48 @@ textInput data =
|
||||||
, label = data.label
|
, label = data.label
|
||||||
, onChange = data.onChange
|
, onChange = data.onChange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Two blocks either next to each other or below each other, depending on the
|
||||||
|
screen shape.
|
||||||
|
-}
|
||||||
|
twoBlocks :
|
||||||
|
{ height : Int
|
||||||
|
, el1 : { height : Int, width : Int } -> Element msg
|
||||||
|
, el2 : { height : Int, width : Int } -> Element msg
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
twoBlocks data =
|
||||||
|
let
|
||||||
|
goesVertical =
|
||||||
|
2 * data.width <= 3 * data.height
|
||||||
|
|
||||||
|
direction =
|
||||||
|
if goesVertical then
|
||||||
|
Element.column
|
||||||
|
|
||||||
|
else
|
||||||
|
Element.row
|
||||||
|
|
||||||
|
width =
|
||||||
|
if goesVertical then
|
||||||
|
data.width
|
||||||
|
|
||||||
|
else
|
||||||
|
data.width // 2
|
||||||
|
|
||||||
|
height =
|
||||||
|
if goesVertical then
|
||||||
|
data.height // 2
|
||||||
|
|
||||||
|
else
|
||||||
|
data.height
|
||||||
|
in
|
||||||
|
direction
|
||||||
|
[ Element.height (Element.px data.height)
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
||||||
|
[ data.el1 { height = height, width = width }
|
||||||
|
, data.el2 { height = height, width = width }
|
||||||
|
]
|
||||||
|
|
229
src/Main.elm
229
src/Main.elm
|
@ -5,13 +5,15 @@ import Browser.Dom
|
||||||
import Browser.Events
|
import Browser.Events
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
import Element.Background
|
import Element.Background
|
||||||
|
import Element.Font
|
||||||
|
import Iddict
|
||||||
|
import Items.VaultList as VaultList
|
||||||
|
import Matrix
|
||||||
|
import Recursion
|
||||||
|
import Screen.Vault as VaultScreen
|
||||||
|
import Screen.Welcome as WelcomeScreen
|
||||||
import Task
|
import Task
|
||||||
import Theme
|
import Theme
|
||||||
import Items.LoginScreen as LoginScreen
|
|
||||||
import Items.VaultScreen as VaultScreen
|
|
||||||
import Items.WelcomeScreen as WelcomeScreen
|
|
||||||
import Element.Font
|
|
||||||
import Items.VaultScreen as VaultScreen
|
|
||||||
|
|
||||||
|
|
||||||
main : Program () Model Msg
|
main : Program () Model Msg
|
||||||
|
@ -28,6 +30,10 @@ main =
|
||||||
-- MODEL
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
type MatrixAction
|
||||||
|
= Sync
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ flavor : Theme.Flavor
|
{ flavor : Theme.Flavor
|
||||||
, height : Int
|
, height : Int
|
||||||
|
@ -35,26 +41,33 @@ type alias Model =
|
||||||
, width : Int
|
, width : Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Screen
|
|
||||||
= ScreenLogin LoginScreen.Model
|
|
||||||
| ScreenWelcome WelcomeScreen.Model
|
|
||||||
| ScreenVault WelcomeScreen.Model Int VaultScreen.Model
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= OnScreenLogin LoginScreen.Msg
|
= OnMatrix MatrixAction Int Matrix.Vault
|
||||||
|
| OnReturnHome Vaults
|
||||||
| OnScreenVault Int VaultScreen.Msg
|
| OnScreenVault Int VaultScreen.Msg
|
||||||
| OnScreenWelcome WelcomeScreen.Msg
|
| OnScreenWelcome WelcomeScreen.Msg
|
||||||
| OnSelectVault Int
|
| OnSelectVault Vaults Int
|
||||||
|
| OnVaultUpdate Int Matrix.VaultUpdate
|
||||||
| Pass
|
| Pass
|
||||||
| ScreenSize { height : Int, width : Int }
|
| ScreenSize { height : Int, width : Int }
|
||||||
| SetFlavor Theme.Flavor
|
| SetFlavor Theme.Flavor
|
||||||
|
|
||||||
|
|
||||||
|
type Screen
|
||||||
|
= ScreenWelcome WelcomeScreen.Model
|
||||||
|
| ScreenVault Vaults Int VaultScreen.Model
|
||||||
|
|
||||||
|
|
||||||
|
type alias Vaults =
|
||||||
|
VaultList.Model
|
||||||
|
|
||||||
|
|
||||||
init : () -> ( Model, Cmd Msg )
|
init : () -> ( Model, Cmd Msg )
|
||||||
init () =
|
init () =
|
||||||
( { flavor = Theme.Latte
|
( { flavor = Theme.Latte
|
||||||
, height = 480
|
, height = 480
|
||||||
, screen = ScreenWelcome (WelcomeScreen.init [])
|
, screen = ScreenWelcome WelcomeScreen.init
|
||||||
, width = 720
|
, width = 720
|
||||||
}
|
}
|
||||||
, Browser.Dom.getViewport
|
, Browser.Dom.getViewport
|
||||||
|
@ -75,33 +88,30 @@ init () =
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
update msg model =
|
update msg model =
|
||||||
case msg of
|
case msg of
|
||||||
OnScreenLogin m ->
|
OnMatrix Sync i vault ->
|
||||||
case model.screen of
|
( model, Matrix.sync (OnVaultUpdate i) vault )
|
||||||
ScreenLogin mdl ->
|
|
||||||
case LoginScreen.update m mdl of
|
OnReturnHome vaults ->
|
||||||
newMdl ->
|
( { model | screen = ScreenWelcome (WelcomeScreen.fromVaults vaults) }
|
||||||
( { model | screen = ScreenLogin newMdl }
|
, Cmd.none
|
||||||
, Cmd.none
|
)
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
( model, Cmd.none )
|
|
||||||
|
|
||||||
OnScreenVault i m ->
|
OnScreenVault i m ->
|
||||||
case model.screen of
|
case model.screen of
|
||||||
ScreenVault welcomeMdl j mdl ->
|
ScreenVault welcomeMdl j mdl ->
|
||||||
if i == j then
|
if i == j then
|
||||||
case VaultScreen.update m mdl of
|
case VaultScreen.update m mdl of
|
||||||
( newMdl, cmd ) ->
|
newMdl ->
|
||||||
( { model | screen = ScreenVault welcomeMdl i newMdl }
|
( { model | screen = ScreenVault welcomeMdl i newMdl }
|
||||||
, Cmd.map (OnScreenVault i) cmd
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
else
|
else
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
OnScreenWelcome m ->
|
OnScreenWelcome m ->
|
||||||
case model.screen of
|
case model.screen of
|
||||||
ScreenWelcome mdl ->
|
ScreenWelcome mdl ->
|
||||||
|
@ -110,28 +120,41 @@ update msg model =
|
||||||
( { model | screen = ScreenWelcome newMdl }
|
( { model | screen = ScreenWelcome newMdl }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
_ ->
|
|
||||||
( model, Cmd.none )
|
|
||||||
|
|
||||||
OnSelectVault i ->
|
|
||||||
case model.screen of
|
|
||||||
ScreenVault welcomeMdl j _ ->
|
|
||||||
if i == j then
|
|
||||||
( model, Cmd.none )
|
|
||||||
else
|
|
||||||
( { model | screen = ScreenVault welcomeMdl i VaultScreen.init }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
ScreenWelcome mdl ->
|
|
||||||
( { model | screen = ScreenVault mdl i VaultScreen.init }
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
OnSelectVault vaults i ->
|
||||||
|
( { model
|
||||||
|
| screen =
|
||||||
|
case model.screen of
|
||||||
|
ScreenWelcome _ ->
|
||||||
|
ScreenVault vaults i VaultScreen.init
|
||||||
|
|
||||||
|
ScreenVault _ j old ->
|
||||||
|
if i == j then
|
||||||
|
ScreenVault vaults j old
|
||||||
|
|
||||||
|
else
|
||||||
|
ScreenVault vaults i VaultScreen.init
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
OnVaultUpdate i vu ->
|
||||||
|
( { model
|
||||||
|
| screen =
|
||||||
|
case model.screen of
|
||||||
|
ScreenWelcome mdl ->
|
||||||
|
WelcomeScreen.updateVault i vu mdl
|
||||||
|
|> ScreenWelcome
|
||||||
|
|
||||||
|
ScreenVault vaults j mdl ->
|
||||||
|
ScreenVault (VaultList.insertVaultUpdate i vu vaults) j mdl
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
Pass ->
|
Pass ->
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
@ -139,7 +162,7 @@ update msg model =
|
||||||
( { model | height = height, width = width }
|
( { model | height = height, width = width }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
SetFlavor flavor ->
|
SetFlavor flavor ->
|
||||||
( { model | flavor = flavor }, Cmd.none )
|
( { model | flavor = flavor }, Cmd.none )
|
||||||
|
|
||||||
|
@ -172,56 +195,72 @@ view model =
|
||||||
|> List.singleton
|
|> List.singleton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
viewScreen : Model -> Element Msg
|
viewScreen : Model -> Element Msg
|
||||||
viewScreen model =
|
viewScreen model =
|
||||||
case model.screen of
|
let
|
||||||
ScreenLogin mdl ->
|
colorBackground =
|
||||||
LoginScreen.view
|
Theme.base model.flavor
|
||||||
{ colorBackground = Theme.base model.flavor
|
|
||||||
, colorMain = Theme.mauve model.flavor
|
|
||||||
, colorMenu = Theme.mantle model.flavor
|
|
||||||
, colorText = Theme.text model.flavor
|
|
||||||
, colorTextField = Theme.surface0 model.flavor
|
|
||||||
, flavor = model.flavor
|
|
||||||
, height = model.height
|
|
||||||
, model = mdl
|
|
||||||
, onFlavorPick = SetFlavor
|
|
||||||
, onSubmit = always Pass
|
|
||||||
, toMsg = OnScreenLogin
|
|
||||||
, width = model.width
|
|
||||||
}
|
|
||||||
|
|
||||||
ScreenVault welcomeMdl i mdl ->
|
|
||||||
case WelcomeScreen.getVault i welcomeMdl of
|
|
||||||
Just vault ->
|
|
||||||
VaultScreen.view
|
|
||||||
{ colorSelectedRoom = Theme.mantle model.flavor
|
|
||||||
, colorText = Theme.text model.flavor
|
|
||||||
, height = model.height
|
|
||||||
, model = mdl
|
|
||||||
, onVaultUpdate = always Pass
|
|
||||||
, toMsg = OnScreenVault i
|
|
||||||
, vault = vault
|
|
||||||
, width = model.width
|
|
||||||
}
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
viewScreen { model | screen = ScreenWelcome welcomeMdl }
|
|
||||||
|
|
||||||
ScreenWelcome mdl ->
|
colorBackground2 =
|
||||||
WelcomeScreen.view
|
Theme.mantle model.flavor
|
||||||
{ colorBackground = Theme.base model.flavor
|
|
||||||
, colorBackground2 = Theme.mantle model.flavor
|
|
||||||
, colorMain = Theme.mauve model.flavor
|
|
||||||
, colorMenu = Theme.surface0 model.flavor
|
|
||||||
, colorText = Theme.text model.flavor
|
|
||||||
, colorTextField = Theme.surface1 model.flavor
|
|
||||||
, flavor = model.flavor
|
|
||||||
, height = model.height
|
|
||||||
, model = mdl
|
|
||||||
, onFlavorPick = SetFlavor
|
|
||||||
, onSelectVault = OnSelectVault
|
|
||||||
, toMsg = OnScreenWelcome
|
|
||||||
, width = model.width
|
|
||||||
}
|
|
||||||
|
|
||||||
|
colorMain =
|
||||||
|
Theme.mauve model.flavor
|
||||||
|
|
||||||
|
colorSurface0 =
|
||||||
|
Theme.surface0 model.flavor
|
||||||
|
|
||||||
|
colorSurface1 =
|
||||||
|
Theme.surface1 model.flavor
|
||||||
|
|
||||||
|
colorText =
|
||||||
|
Theme.text model.flavor
|
||||||
|
in
|
||||||
|
Recursion.runRecursion
|
||||||
|
(\screen ->
|
||||||
|
case screen of
|
||||||
|
ScreenVault vaults i mdl ->
|
||||||
|
case Iddict.get i vaults of
|
||||||
|
Just block ->
|
||||||
|
VaultScreen.view
|
||||||
|
{ colorBackground = colorBackground
|
||||||
|
, colorBackground2 = colorBackground2
|
||||||
|
, colorMain = colorMain
|
||||||
|
, colorText = colorText
|
||||||
|
, height = model.height
|
||||||
|
, logs = block.logs
|
||||||
|
, model = mdl
|
||||||
|
, onReturnToMenu = OnReturnHome vaults
|
||||||
|
, onSync = OnMatrix Sync i block.vault
|
||||||
|
, onVaultUpdate = OnVaultUpdate i
|
||||||
|
, toMsg = OnScreenVault i
|
||||||
|
, vault = block.vault
|
||||||
|
, width = model.width
|
||||||
|
}
|
||||||
|
|> Recursion.base
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
WelcomeScreen.fromVaults vaults
|
||||||
|
|> ScreenWelcome
|
||||||
|
|> Recursion.recurse
|
||||||
|
|
||||||
|
ScreenWelcome mdl ->
|
||||||
|
WelcomeScreen.view
|
||||||
|
{ colorBackground = colorBackground
|
||||||
|
, colorBackground2 = colorBackground2
|
||||||
|
, colorMain = colorMain
|
||||||
|
, colorMenu = colorSurface0
|
||||||
|
, colorText = colorText
|
||||||
|
, colorTextField = colorSurface1
|
||||||
|
, flavor = model.flavor
|
||||||
|
, height = model.height
|
||||||
|
, model = mdl
|
||||||
|
, onFlavorPick = SetFlavor
|
||||||
|
, onSelectVault = OnSelectVault
|
||||||
|
, toMsg = OnScreenWelcome
|
||||||
|
, width = model.width
|
||||||
|
}
|
||||||
|
|> Recursion.base
|
||||||
|
)
|
||||||
|
model.screen
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
module Screen.Vault exposing (..)
|
||||||
|
|
||||||
|
import Color exposing (Color)
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Items.LogViewer as LogViewer
|
||||||
|
import Layout
|
||||||
|
import Material.Icons
|
||||||
|
import Matrix
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
Screen
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= GoToScreen Screen
|
||||||
|
|
||||||
|
|
||||||
|
type Screen
|
||||||
|
= Home
|
||||||
|
| Logs
|
||||||
|
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
Home
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> Model
|
||||||
|
update msg _ =
|
||||||
|
case msg of
|
||||||
|
GoToScreen screen ->
|
||||||
|
screen
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view :
|
||||||
|
{ colorBackground : Color
|
||||||
|
, colorBackground2 : Color
|
||||||
|
, colorMain : Color
|
||||||
|
, colorText : Color
|
||||||
|
, height : Int
|
||||||
|
, logs : List { channel : String, content : String }
|
||||||
|
, model : Model
|
||||||
|
, onReturnToMenu : msg
|
||||||
|
, onSync : msg
|
||||||
|
, onVaultUpdate : Matrix.VaultUpdate -> msg
|
||||||
|
, toMsg : Msg -> msg
|
||||||
|
, vault : Matrix.Vault
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
view data =
|
||||||
|
Element.row
|
||||||
|
[ Element.height (Element.px data.height)
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
||||||
|
[ Layout.sideIconBar
|
||||||
|
{ colorBackground = data.colorBackground2
|
||||||
|
, colorText = data.colorText
|
||||||
|
, height = data.height
|
||||||
|
, items =
|
||||||
|
[ { icon = Layout.iconAsIcon Material.Icons.arrow_back
|
||||||
|
, onPress = data.onReturnToMenu
|
||||||
|
, text = "Return to menu"
|
||||||
|
}
|
||||||
|
, { icon = Layout.iconAsIcon Material.Icons.home
|
||||||
|
, onPress = data.toMsg (GoToScreen Home)
|
||||||
|
, text = "Home"
|
||||||
|
}
|
||||||
|
, { icon = Layout.iconAsIcon Material.Icons.inbox
|
||||||
|
, onPress = data.toMsg (GoToScreen Logs)
|
||||||
|
, text = "Logs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, width = 100
|
||||||
|
}
|
||||||
|
, viewContent
|
||||||
|
{ colorMain = data.colorMain
|
||||||
|
, colorText = data.colorText
|
||||||
|
, height = data.height
|
||||||
|
, logs = data.logs
|
||||||
|
, model = data.model
|
||||||
|
, onSync = data.onSync
|
||||||
|
, vault = data.vault
|
||||||
|
, width = data.width - 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewContent :
|
||||||
|
{ colorMain : Color
|
||||||
|
, colorText : Color
|
||||||
|
, height : Int
|
||||||
|
, logs : List { channel : String, content : String }
|
||||||
|
, model : Model
|
||||||
|
, onSync : msg
|
||||||
|
, vault : Matrix.Vault
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
viewContent data =
|
||||||
|
let
|
||||||
|
paddingSize =
|
||||||
|
30
|
||||||
|
in
|
||||||
|
Element.el [ Element.padding paddingSize ]
|
||||||
|
(case data.model of
|
||||||
|
Logs ->
|
||||||
|
LogViewer.viewRecent
|
||||||
|
{ height = data.height - 2 * paddingSize
|
||||||
|
, logs = data.logs
|
||||||
|
, width = data.width - 2 * paddingSize
|
||||||
|
}
|
||||||
|
|
||||||
|
Home ->
|
||||||
|
viewStartMenu
|
||||||
|
{ colorMain = data.colorMain
|
||||||
|
, colorText = data.colorText
|
||||||
|
, height = data.height - 2 * paddingSize
|
||||||
|
, onSync = data.onSync
|
||||||
|
, width = data.width - 2 * paddingSize
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
viewStartMenu :
|
||||||
|
{ colorMain : Color
|
||||||
|
, colorText : Color
|
||||||
|
, height : Int
|
||||||
|
, onSync : msg
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
viewStartMenu data =
|
||||||
|
Element.column
|
||||||
|
[ Element.height (Element.px data.height)
|
||||||
|
, Element.spacing 5
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
||||||
|
[ Layout.header "Start Menu"
|
||||||
|
, Layout.stdText "The elm-matrix-sdk vault uses the /sync endpoint to get the latest updates. Make sure to run this function to get the latest information."
|
||||||
|
, Layout.containedButton
|
||||||
|
{ buttonColor = data.colorMain
|
||||||
|
, clickColor = data.colorText
|
||||||
|
, icon = always Element.none
|
||||||
|
, onPress = Just data.onSync
|
||||||
|
, text = "SYNC"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,179 @@
|
||||||
|
module Screen.Welcome exposing (..)
|
||||||
|
|
||||||
|
import Color exposing (Color)
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Items.Introduction as Introduction
|
||||||
|
import Items.ItemPicker as ItemPicker
|
||||||
|
import Items.LoginView as LoginView
|
||||||
|
import Items.VaultList as VaultList
|
||||||
|
import Layout
|
||||||
|
import Matrix
|
||||||
|
import Matrix.Settings
|
||||||
|
import Theme
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ login : Maybe LoginView.Model
|
||||||
|
, vaults : ItemPicker.Model VaultList.VaultBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= OnAddNew
|
||||||
|
| OnLogin LoginView.Msg
|
||||||
|
| OnSubmitVault { name : String, vault : Matrix.Vault }
|
||||||
|
| OnVaultUpdate Int Matrix.VaultUpdate
|
||||||
|
| OnVaults ItemPicker.Msg
|
||||||
|
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
{ login = Nothing -- Just LoginView.init
|
||||||
|
, vaults = ItemPicker.init VaultList.init
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fromVaults : VaultList.Model -> Model
|
||||||
|
fromVaults items =
|
||||||
|
{ login = Nothing
|
||||||
|
, vaults = ItemPicker.init items
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> Model
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
OnAddNew ->
|
||||||
|
{ model | login = Just LoginView.init }
|
||||||
|
|
||||||
|
OnLogin m ->
|
||||||
|
{ model | login = Maybe.map (LoginView.update m) model.login }
|
||||||
|
|
||||||
|
OnSubmitVault block ->
|
||||||
|
{ login = Nothing
|
||||||
|
, vaults = ItemPicker.updateContent (VaultList.addVault block) model.vaults
|
||||||
|
}
|
||||||
|
|
||||||
|
OnVaultUpdate i vu ->
|
||||||
|
{ model | vaults = ItemPicker.updateContent (VaultList.insertVaultUpdate i vu) model.vaults }
|
||||||
|
|
||||||
|
OnVaults m ->
|
||||||
|
{ model | vaults = ItemPicker.update m model.vaults }
|
||||||
|
|
||||||
|
|
||||||
|
updateVault : Int -> Matrix.VaultUpdate -> Model -> Model
|
||||||
|
updateVault i vu =
|
||||||
|
update (OnVaultUpdate i vu)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view :
|
||||||
|
{ colorBackground : Color
|
||||||
|
, colorBackground2 : Color
|
||||||
|
, colorMain : Color
|
||||||
|
, colorMenu : Color
|
||||||
|
, colorText : Color
|
||||||
|
, colorTextField : Color
|
||||||
|
, flavor : Theme.Flavor
|
||||||
|
, height : Int
|
||||||
|
, model : Model
|
||||||
|
, onFlavorPick : Theme.Flavor -> msg
|
||||||
|
, onSelectVault : VaultList.Model -> Int -> msg
|
||||||
|
, toMsg : Msg -> msg
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
view data =
|
||||||
|
let
|
||||||
|
onSelectVault =
|
||||||
|
data.onSelectVault (ItemPicker.extract data.model.vaults)
|
||||||
|
in
|
||||||
|
case data.model.login of
|
||||||
|
Just login ->
|
||||||
|
Layout.twoBlocks
|
||||||
|
{ height = data.height
|
||||||
|
, el1 =
|
||||||
|
\{ height, width } ->
|
||||||
|
viewIntroduction
|
||||||
|
{ colorBackground = data.colorBackground
|
||||||
|
, colorMenu = data.colorMenu
|
||||||
|
, colorText = data.colorText
|
||||||
|
, height = height
|
||||||
|
, model = data.model.vaults
|
||||||
|
, onAddNew = Nothing
|
||||||
|
, onSelectVault = onSelectVault
|
||||||
|
, width = width
|
||||||
|
}
|
||||||
|
, el2 =
|
||||||
|
\{ height, width } ->
|
||||||
|
LoginView.view
|
||||||
|
{ colorBackground = data.colorBackground2
|
||||||
|
, colorMain = data.colorMain
|
||||||
|
, colorMenu = data.colorMenu
|
||||||
|
, colorText = data.colorText
|
||||||
|
, colorTextField = data.colorTextField
|
||||||
|
, height = height
|
||||||
|
, flavor = data.flavor
|
||||||
|
, model = login
|
||||||
|
, onFlavorPick = data.onFlavorPick
|
||||||
|
, onSubmit = \vault -> OnSubmitVault { name = "New Vault", vault = vault } |> data.toMsg
|
||||||
|
, toMsg = OnLogin >> data.toMsg
|
||||||
|
, width = width
|
||||||
|
}
|
||||||
|
, width = data.width
|
||||||
|
}
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
viewIntroduction
|
||||||
|
{ colorBackground = data.colorBackground
|
||||||
|
, colorMenu = data.colorMenu
|
||||||
|
, colorText = data.colorText
|
||||||
|
, height = data.height
|
||||||
|
, model = data.model.vaults
|
||||||
|
, onAddNew = Just (data.toMsg OnAddNew)
|
||||||
|
, onSelectVault = onSelectVault
|
||||||
|
, width = data.width
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewIntroduction :
|
||||||
|
{ colorBackground : Color
|
||||||
|
, colorMenu : Color
|
||||||
|
, colorText : Color
|
||||||
|
, height : Int
|
||||||
|
, model : ItemPicker.Model VaultList.VaultBlock
|
||||||
|
, onAddNew : Maybe msg
|
||||||
|
, onSelectVault : Int -> msg
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
viewIntroduction data =
|
||||||
|
[ Introduction.view { colorBackground = data.colorBackground, width = data.width }
|
||||||
|
, ItemPicker.view
|
||||||
|
{ colorMenu = data.colorMenu
|
||||||
|
, colorText = data.colorText
|
||||||
|
, height = data.height
|
||||||
|
, model = data.model
|
||||||
|
, toText = \_ vb -> Matrix.Settings.getDeviceName vb.vault
|
||||||
|
, toTitle = \vid vb -> vb.name ++ " #" ++ String.fromInt (vid + 1)
|
||||||
|
, onAddNew = data.onAddNew
|
||||||
|
, onClick = data.onSelectVault
|
||||||
|
, width = data.width
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|> Element.column
|
||||||
|
[ Element.height (Element.px data.height)
|
||||||
|
, Element.scrollbarY
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
Loading…
Reference in New Issue