Add initial spike
parent
4e76fe6e09
commit
3b370138aa
|
@ -0,0 +1,43 @@
|
||||||
|
module Items.FlavorPicker exposing (..)
|
||||||
|
{-| This module allows the user to pick whatever flavor they want to use.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Color exposing (Color)
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Html.Attributes
|
||||||
|
import Material.Icons
|
||||||
|
import Theme exposing (Flavor(..))
|
||||||
|
import Layout
|
||||||
|
import Element.Events
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
view :
|
||||||
|
{ height : Int
|
||||||
|
, flavor : Flavor
|
||||||
|
, onClick : Flavor -> msg
|
||||||
|
, themeIcon : Flavor -> Color
|
||||||
|
, width : Int
|
||||||
|
} -> Element msg
|
||||||
|
view data =
|
||||||
|
let
|
||||||
|
lightMode = data.flavor == Latte
|
||||||
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
module Items.Introduction exposing (..)
|
||||||
|
|
||||||
|
|
||||||
|
import Color exposing (Color)
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Element.Background
|
||||||
|
import Widget.Material.Typography
|
||||||
|
import Theme
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
type alias Model = ()
|
||||||
|
|
||||||
|
type alias Msg = ()
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
-- SUBSCRIPTIONS
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
view :
|
||||||
|
{ colorBackground : Color
|
||||||
|
, width : Int
|
||||||
|
} -> Element msg
|
||||||
|
view data =
|
||||||
|
[ header "Martiplier"
|
||||||
|
, text "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."
|
||||||
|
]
|
||||||
|
|> Element.column
|
||||||
|
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
|
||||||
|
, Element.padding 30
|
||||||
|
, Element.spacing 20
|
||||||
|
, 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,198 @@
|
||||||
|
module Items.LoginScreen exposing (..)
|
||||||
|
{-| The Login screen allows the user to log in, as well as view a short display
|
||||||
|
of what to expect from the Matrix client.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Color exposing (Color)
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Element.Background
|
||||||
|
import Element.Border
|
||||||
|
import Matrix
|
||||||
|
import Matrix.Settings
|
||||||
|
import Theme
|
||||||
|
import Layout
|
||||||
|
import Items.FlavorPicker as FlavorPicker
|
||||||
|
import Material.Icons
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ accessToken : String
|
||||||
|
, loginMethod : LoginMethod
|
||||||
|
, password : String
|
||||||
|
, username : String
|
||||||
|
}
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= SetAccessToken String
|
||||||
|
| SetPassword String
|
||||||
|
| SetUsername String
|
||||||
|
| SwitchMethod LoginMethod
|
||||||
|
|
||||||
|
type LoginMethod
|
||||||
|
= AccessToken
|
||||||
|
| Password
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
{ accessToken = ""
|
||||||
|
, loginMethod = AccessToken
|
||||||
|
, password = ""
|
||||||
|
, username = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
update : Msg -> Model -> Model
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
SetAccessToken name ->
|
||||||
|
{ model | accessToken = name }
|
||||||
|
|
||||||
|
SetPassword name ->
|
||||||
|
{ model | password = name }
|
||||||
|
|
||||||
|
SetUsername name ->
|
||||||
|
{ model | username = name }
|
||||||
|
|
||||||
|
SwitchMethod method ->
|
||||||
|
{ model | loginMethod = method }
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
view :
|
||||||
|
{ colorBackground : Color
|
||||||
|
, colorMain : Color
|
||||||
|
, colorMenu : Color
|
||||||
|
, colorText : Color
|
||||||
|
, colorTextField : Color
|
||||||
|
, height : Int
|
||||||
|
, flavor : Theme.Flavor
|
||||||
|
, model : Model
|
||||||
|
, onFlavorPick : Theme.Flavor -> msg
|
||||||
|
, onSubmit : Matrix.Vault -> msg
|
||||||
|
, toMsg : Msg -> msg
|
||||||
|
, width : Int
|
||||||
|
} -> Element msg
|
||||||
|
view data =
|
||||||
|
[ viewLoginMethodPicker
|
||||||
|
{ color = data.colorMain
|
||||||
|
, loginMethod = data.model.loginMethod
|
||||||
|
, toMsg = SwitchMethod >> data.toMsg
|
||||||
|
}
|
||||||
|
, Layout.textInput
|
||||||
|
{ color = data.colorTextField
|
||||||
|
, label = "username"
|
||||||
|
, onChange = SetUsername >> data.toMsg
|
||||||
|
, placeholder = Just "@alice:example.org"
|
||||||
|
, text = data.model.username
|
||||||
|
}
|
||||||
|
, case data.model.loginMethod of
|
||||||
|
AccessToken ->
|
||||||
|
Layout.passwordInput
|
||||||
|
{ color = data.colorTextField
|
||||||
|
, label = "access-token"
|
||||||
|
, onChange = SetAccessToken >> data.toMsg
|
||||||
|
, placeholder = Just "syt_p12smN_aR0KbfXxDr3RVESXNr3I_k5Ay88"
|
||||||
|
, show = False
|
||||||
|
, text = data.model.accessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
Password ->
|
||||||
|
Layout.passwordInput
|
||||||
|
{ color = data.colorTextField
|
||||||
|
, label = "password"
|
||||||
|
, onChange = SetPassword >> data.toMsg
|
||||||
|
, placeholder = Just "Password"
|
||||||
|
, show = False
|
||||||
|
, text = data.model.password
|
||||||
|
}
|
||||||
|
, Layout.containedButton
|
||||||
|
{ buttonColor = data.colorMain
|
||||||
|
, clickColor = data.colorText
|
||||||
|
, icon = always Element.none
|
||||||
|
, onPress =
|
||||||
|
data.model
|
||||||
|
|> toVault
|
||||||
|
|> Maybe.map data.onSubmit
|
||||||
|
, text = "LOG IN"
|
||||||
|
}
|
||||||
|
|> Element.el
|
||||||
|
[ Element.centerX ]
|
||||||
|
]
|
||||||
|
|> Element.column
|
||||||
|
[ Element.Background.color (Theme.toElmUiColor data.colorMenu)
|
||||||
|
, Element.Border.rounded 25
|
||||||
|
, Element.centerX
|
||||||
|
, Element.centerY
|
||||||
|
, Element.height (Element.px 300)
|
||||||
|
, Element.padding 30
|
||||||
|
, Element.spaceEvenly
|
||||||
|
, Element.width (Element.px 400)
|
||||||
|
]
|
||||||
|
|> 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 ]
|
||||||
|
)
|
||||||
|
, Element.height (Element.px data.height)
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
||||||
|
|
||||||
|
toVault : Model -> Maybe Matrix.Vault
|
||||||
|
toVault model =
|
||||||
|
case model.loginMethod of
|
||||||
|
AccessToken ->
|
||||||
|
if model.accessToken == "" then
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
else
|
||||||
|
model.username
|
||||||
|
|> Matrix.fromUserId
|
||||||
|
|> Maybe.map (Matrix.Settings.setAccessToken model.accessToken)
|
||||||
|
|
||||||
|
Password ->
|
||||||
|
if model.password == "" then
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
else
|
||||||
|
model.username
|
||||||
|
|> Matrix.fromUserId
|
||||||
|
|> Maybe.map (Matrix.Settings.setPassword model.password)
|
||||||
|
|
||||||
|
viewLoginMethodPicker : { color : Color, loginMethod : LoginMethod, toMsg : LoginMethod -> msg } -> Element msg
|
||||||
|
viewLoginMethodPicker data =
|
||||||
|
Layout.tab
|
||||||
|
{ color = data.color
|
||||||
|
, content = always Element.none
|
||||||
|
, items =
|
||||||
|
[ { icon = Layout.iconAsIcon Material.Icons.key
|
||||||
|
, text = "Access token"
|
||||||
|
}
|
||||||
|
, { icon = Layout.iconAsIcon Material.Icons.password
|
||||||
|
, text = "Password"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, onSelect =
|
||||||
|
(\i ->
|
||||||
|
if i == 0 then
|
||||||
|
data.toMsg AccessToken
|
||||||
|
else
|
||||||
|
data.toMsg Password
|
||||||
|
)
|
||||||
|
, selected =
|
||||||
|
case data.loginMethod of
|
||||||
|
AccessToken ->
|
||||||
|
0
|
||||||
|
|
||||||
|
Password ->
|
||||||
|
1
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
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) ]
|
|
@ -0,0 +1,170 @@
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
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)
|
||||||
|
]
|
|
@ -0,0 +1,277 @@
|
||||||
|
module Layout exposing
|
||||||
|
( tab
|
||||||
|
, iconAsElement, iconAsIcon
|
||||||
|
, containedButton, outlinedButton, textButton, sideList
|
||||||
|
, textInput, passwordInput
|
||||||
|
, loadingIndicator, itemWithSubtext
|
||||||
|
)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
|
||||||
|
# Layout
|
||||||
|
|
||||||
|
The layout module exposes some boilerplate functions that have produce a
|
||||||
|
beautiful Material design Elm webpage.
|
||||||
|
|
||||||
|
|
||||||
|
## Elements
|
||||||
|
|
||||||
|
@docs tab
|
||||||
|
|
||||||
|
## Icons
|
||||||
|
|
||||||
|
@docs iconAsElement, iconAsIcon
|
||||||
|
|
||||||
|
## Buttons
|
||||||
|
|
||||||
|
@docs containedButton, outlinedButton, textButton
|
||||||
|
|
||||||
|
|
||||||
|
## Text fields
|
||||||
|
|
||||||
|
@docs textInput, passwordInput
|
||||||
|
|
||||||
|
## Items in a list
|
||||||
|
|
||||||
|
@docs itemWithSubtext
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
|
||||||
|
@docs sideList
|
||||||
|
|
||||||
|
## Other elements
|
||||||
|
|
||||||
|
@docs loadingIndicator
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Color exposing (Color)
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Element.Input
|
||||||
|
import Widget
|
||||||
|
import Widget.Customize as Customize
|
||||||
|
import Widget.Icon exposing (Icon)
|
||||||
|
import Widget.Material as Material
|
||||||
|
import Material.Icons.Types
|
||||||
|
|
||||||
|
|
||||||
|
{-| A contained button representing the most important action of a group.
|
||||||
|
-}
|
||||||
|
containedButton :
|
||||||
|
{ buttonColor : Color
|
||||||
|
, clickColor : Color
|
||||||
|
, icon : Icon msg
|
||||||
|
, onPress : Maybe msg
|
||||||
|
, text : String
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
containedButton data =
|
||||||
|
Widget.button
|
||||||
|
({ primary = data.buttonColor, onPrimary = data.clickColor }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.containedButton
|
||||||
|
|> Customize.elementButton [ Element.width Element.fill ]
|
||||||
|
)
|
||||||
|
{ text = data.text, icon = data.icon, onPress = data.onPress }
|
||||||
|
|
||||||
|
iconAsElement :
|
||||||
|
{ color : Color
|
||||||
|
, height : Int
|
||||||
|
, icon : Material.Icons.Types.Icon msg
|
||||||
|
, width : Int
|
||||||
|
} -> Element msg
|
||||||
|
iconAsElement data =
|
||||||
|
data.icon
|
||||||
|
|> iconAsIcon
|
||||||
|
|> (|>) { size = Basics.min data.height data.width, color = data.color }
|
||||||
|
|> Element.el [ Element.centerX, Element.centerY ]
|
||||||
|
|> Element.el
|
||||||
|
[ Element.height (Element.px data.height)
|
||||||
|
, Element.width (Element.px data.width)
|
||||||
|
]
|
||||||
|
|
||||||
|
iconAsIcon : Material.Icons.Types.Icon msg -> Widget.Icon.Icon msg
|
||||||
|
iconAsIcon =
|
||||||
|
Widget.Icon.elmMaterialIcons Material.Icons.Types.Color
|
||||||
|
|
||||||
|
{-| Multiline item
|
||||||
|
-}
|
||||||
|
itemWithSubtext :
|
||||||
|
{ color : Color
|
||||||
|
, leftIcon : Widget.Icon.Icon msg
|
||||||
|
, onPress : Maybe msg
|
||||||
|
, rightIcon : Widget.Icon.Icon msg
|
||||||
|
, text : String
|
||||||
|
, title : String
|
||||||
|
}
|
||||||
|
-> Widget.Item msg
|
||||||
|
itemWithSubtext data =
|
||||||
|
Widget.multiLineItem
|
||||||
|
( { primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.multiLineItem
|
||||||
|
)
|
||||||
|
{ content = data.rightIcon
|
||||||
|
, icon = data.leftIcon
|
||||||
|
, onPress = data.onPress
|
||||||
|
, title = data.title
|
||||||
|
, text = data.text
|
||||||
|
}
|
||||||
|
|
||||||
|
{-| Circular loading bar indicator
|
||||||
|
-}
|
||||||
|
loadingIndicator :
|
||||||
|
{ color : Color
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
loadingIndicator data =
|
||||||
|
Widget.circularProgressIndicator
|
||||||
|
( { primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.progressIndicator
|
||||||
|
)
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
{-| An outlined button representing an important action within a group.
|
||||||
|
-}
|
||||||
|
outlinedButton :
|
||||||
|
{ color : Color
|
||||||
|
, icon : Icon msg
|
||||||
|
, onPress : Maybe msg
|
||||||
|
, text : String
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
outlinedButton data =
|
||||||
|
Widget.button
|
||||||
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.outlinedButton
|
||||||
|
)
|
||||||
|
{ text = data.text, icon = data.icon, onPress = data.onPress }
|
||||||
|
|
||||||
|
{-| Show a password field
|
||||||
|
-}
|
||||||
|
passwordInput :
|
||||||
|
{ color : Color
|
||||||
|
, label : String
|
||||||
|
, onChange : String -> msg
|
||||||
|
, placeholder : Maybe String
|
||||||
|
, show : Bool
|
||||||
|
, text : String
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
passwordInput data =
|
||||||
|
Widget.currentPasswordInputV2
|
||||||
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.passwordInput
|
||||||
|
|> Customize.elementRow [ Element.width Element.fill ]
|
||||||
|
)
|
||||||
|
{ label = data.label
|
||||||
|
, onChange = data.onChange
|
||||||
|
, placeholder =
|
||||||
|
data.placeholder
|
||||||
|
|> Maybe.map Element.text
|
||||||
|
|> Maybe.map (Element.Input.placeholder [])
|
||||||
|
, show = data.show
|
||||||
|
, text = data.text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Create a simple palette.
|
||||||
|
-}
|
||||||
|
singlePalette : { primary : Color, onPrimary : Color } -> Material.Palette
|
||||||
|
singlePalette { primary, onPrimary } =
|
||||||
|
{ primary = primary
|
||||||
|
, secondary = primary
|
||||||
|
, background = primary
|
||||||
|
, surface = primary
|
||||||
|
, error = primary
|
||||||
|
, on =
|
||||||
|
{ primary = onPrimary
|
||||||
|
, secondary = onPrimary
|
||||||
|
, background = onPrimary
|
||||||
|
, surface = onPrimary
|
||||||
|
, error = onPrimary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sideList : { color : Color, items : List (Widget.Item msg) }-> Element msg
|
||||||
|
sideList data =
|
||||||
|
Widget.itemList
|
||||||
|
( { primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.sideSheet
|
||||||
|
)
|
||||||
|
data.items
|
||||||
|
|
||||||
|
{-| A tab selector that always has an item selected.
|
||||||
|
-}
|
||||||
|
tab :
|
||||||
|
{ color : Color
|
||||||
|
, content : Int -> Element msg
|
||||||
|
, items : List { text : String, icon : Icon msg }
|
||||||
|
, onSelect : Int -> msg
|
||||||
|
, selected : Int
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
tab data =
|
||||||
|
Widget.tab
|
||||||
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.tab
|
||||||
|
)
|
||||||
|
{ tabs =
|
||||||
|
{ onSelect = data.onSelect >> Just
|
||||||
|
, options = data.items
|
||||||
|
, selected = Just data.selected
|
||||||
|
}
|
||||||
|
, content = \_ -> data.content data.selected
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| A text button representing an important action within a group.
|
||||||
|
-}
|
||||||
|
textButton :
|
||||||
|
{ icon : Icon msg
|
||||||
|
, onPress : Maybe msg
|
||||||
|
, text : String
|
||||||
|
, color : Color
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
textButton data =
|
||||||
|
Widget.button
|
||||||
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.textButton
|
||||||
|
)
|
||||||
|
{ text = data.text, icon = data.icon, onPress = data.onPress }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Text input element.
|
||||||
|
-}
|
||||||
|
textInput :
|
||||||
|
{ color : Color
|
||||||
|
, label : String
|
||||||
|
, onChange : String -> msg
|
||||||
|
, placeholder : Maybe String
|
||||||
|
, text : String
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
textInput data =
|
||||||
|
Widget.textInput
|
||||||
|
({ primary = data.color, onPrimary = data.color }
|
||||||
|
|> singlePalette
|
||||||
|
|> Material.textInput
|
||||||
|
|> Customize.elementRow [ Element.width Element.fill ]
|
||||||
|
)
|
||||||
|
{ chips = []
|
||||||
|
, text = data.text
|
||||||
|
, placeholder =
|
||||||
|
data.placeholder
|
||||||
|
|> Maybe.map Element.text
|
||||||
|
|> Maybe.map (Element.Input.placeholder [])
|
||||||
|
, label = data.label
|
||||||
|
, onChange = data.onChange
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
module Main exposing (..)
|
||||||
|
|
||||||
|
import Browser
|
||||||
|
import Browser.Dom
|
||||||
|
import Browser.Events
|
||||||
|
import Element exposing (Element)
|
||||||
|
import Element.Background
|
||||||
|
import Task
|
||||||
|
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 =
|
||||||
|
Browser.document
|
||||||
|
{ init = init
|
||||||
|
, subscriptions = subscriptions
|
||||||
|
, update = update
|
||||||
|
, view = view
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ flavor : Theme.Flavor
|
||||||
|
, height : Int
|
||||||
|
, screen : Screen
|
||||||
|
, width : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Screen
|
||||||
|
= ScreenLogin LoginScreen.Model
|
||||||
|
| ScreenWelcome WelcomeScreen.Model
|
||||||
|
| ScreenVault WelcomeScreen.Model Int VaultScreen.Model
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= OnScreenLogin LoginScreen.Msg
|
||||||
|
| OnScreenVault Int VaultScreen.Msg
|
||||||
|
| OnScreenWelcome WelcomeScreen.Msg
|
||||||
|
| OnSelectVault Int
|
||||||
|
| Pass
|
||||||
|
| ScreenSize { height : Int, width : Int }
|
||||||
|
| SetFlavor Theme.Flavor
|
||||||
|
|
||||||
|
|
||||||
|
init : () -> ( Model, Cmd Msg )
|
||||||
|
init () =
|
||||||
|
( { flavor = Theme.Latte
|
||||||
|
, height = 480
|
||||||
|
, screen = ScreenWelcome (WelcomeScreen.init [])
|
||||||
|
, width = 720
|
||||||
|
}
|
||||||
|
, Browser.Dom.getViewport
|
||||||
|
|> Task.perform
|
||||||
|
(\viewport ->
|
||||||
|
ScreenSize
|
||||||
|
{ height = floor viewport.viewport.height
|
||||||
|
, width = floor viewport.viewport.width
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
OnScreenLogin m ->
|
||||||
|
case model.screen of
|
||||||
|
ScreenLogin mdl ->
|
||||||
|
case LoginScreen.update m mdl of
|
||||||
|
newMdl ->
|
||||||
|
( { model | screen = ScreenLogin newMdl }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
OnScreenVault i m ->
|
||||||
|
case model.screen of
|
||||||
|
ScreenVault welcomeMdl j mdl ->
|
||||||
|
if i == j then
|
||||||
|
case VaultScreen.update m mdl of
|
||||||
|
( newMdl, cmd ) ->
|
||||||
|
( { model | screen = ScreenVault welcomeMdl i newMdl }
|
||||||
|
, Cmd.map (OnScreenVault i) cmd
|
||||||
|
)
|
||||||
|
else
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
OnScreenWelcome m ->
|
||||||
|
case model.screen of
|
||||||
|
ScreenWelcome mdl ->
|
||||||
|
case WelcomeScreen.update m mdl of
|
||||||
|
newMdl ->
|
||||||
|
( { model | screen = ScreenWelcome newMdl }
|
||||||
|
, 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 )
|
||||||
|
|
||||||
|
Pass ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
ScreenSize { height, width } ->
|
||||||
|
( { model | height = height, width = width }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
SetFlavor flavor ->
|
||||||
|
( { model | flavor = flavor }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- SUBSCRIPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions _ =
|
||||||
|
Sub.batch
|
||||||
|
[ Browser.Events.onResize (\w h -> ScreenSize { width = w, height = h })
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Browser.Document Msg
|
||||||
|
view model =
|
||||||
|
{ title = "Matrix Plier"
|
||||||
|
, body =
|
||||||
|
model
|
||||||
|
|> viewScreen
|
||||||
|
|> Element.layout
|
||||||
|
[ Element.Background.color (Theme.baseUI model.flavor)
|
||||||
|
, Element.Font.color (Theme.textUI model.flavor)
|
||||||
|
]
|
||||||
|
|> List.singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
viewScreen : Model -> Element Msg
|
||||||
|
viewScreen model =
|
||||||
|
case model.screen of
|
||||||
|
ScreenLogin mdl ->
|
||||||
|
LoginScreen.view
|
||||||
|
{ 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 ->
|
||||||
|
WelcomeScreen.view
|
||||||
|
{ 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
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,583 @@
|
||||||
|
module Theme exposing (..)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
|
||||||
|
# Theme
|
||||||
|
|
||||||
|
The Theme helps pick colors from different color schemes.
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Catppuccin.Frappe as CF
|
||||||
|
import Catppuccin.Latte as CL
|
||||||
|
import Catppuccin.Macchiato as CA
|
||||||
|
import Catppuccin.Mocha as CO
|
||||||
|
import Color
|
||||||
|
import Element
|
||||||
|
import Element.Background
|
||||||
|
|
||||||
|
|
||||||
|
{-| Catppuccin flavor used for display.
|
||||||
|
-}
|
||||||
|
type Flavor
|
||||||
|
= Frappe
|
||||||
|
| Latte
|
||||||
|
| Macchiato
|
||||||
|
| Mocha
|
||||||
|
|
||||||
|
|
||||||
|
background : (Flavor -> Color.Color) -> Flavor -> Element.Attribute msg
|
||||||
|
background toColor =
|
||||||
|
toColor >> toElmUiColor >> Element.Background.color
|
||||||
|
|
||||||
|
|
||||||
|
toElmUiColor : Color.Color -> Element.Color
|
||||||
|
toElmUiColor =
|
||||||
|
Color.toRgba >> Element.fromRgb
|
||||||
|
|
||||||
|
|
||||||
|
rosewater : Flavor -> Color.Color
|
||||||
|
rosewater flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.rosewater
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.rosewater
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.rosewater
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.rosewater
|
||||||
|
|
||||||
|
|
||||||
|
rosewaterUI : Flavor -> Element.Color
|
||||||
|
rosewaterUI =
|
||||||
|
rosewater >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
flamingo : Flavor -> Color.Color
|
||||||
|
flamingo flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.flamingo
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.flamingo
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.flamingo
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.flamingo
|
||||||
|
|
||||||
|
|
||||||
|
flamingoUI : Flavor -> Element.Color
|
||||||
|
flamingoUI =
|
||||||
|
flamingo >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
pink : Flavor -> Color.Color
|
||||||
|
pink flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.pink
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.pink
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.pink
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.pink
|
||||||
|
|
||||||
|
|
||||||
|
pinkUI : Flavor -> Element.Color
|
||||||
|
pinkUI =
|
||||||
|
pink >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
mauve : Flavor -> Color.Color
|
||||||
|
mauve flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.mauve
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.mauve
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.mauve
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.mauve
|
||||||
|
|
||||||
|
|
||||||
|
mauveUI : Flavor -> Element.Color
|
||||||
|
mauveUI =
|
||||||
|
mauve >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
red : Flavor -> Color.Color
|
||||||
|
red flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.red
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.red
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.red
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.red
|
||||||
|
|
||||||
|
|
||||||
|
redUI : Flavor -> Element.Color
|
||||||
|
redUI =
|
||||||
|
red >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
maroon : Flavor -> Color.Color
|
||||||
|
maroon flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.maroon
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.maroon
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.maroon
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.maroon
|
||||||
|
|
||||||
|
|
||||||
|
maroonUI : Flavor -> Element.Color
|
||||||
|
maroonUI =
|
||||||
|
maroon >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
peach : Flavor -> Color.Color
|
||||||
|
peach flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.peach
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.peach
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.peach
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.peach
|
||||||
|
|
||||||
|
|
||||||
|
peachUI : Flavor -> Element.Color
|
||||||
|
peachUI =
|
||||||
|
peach >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
yellow : Flavor -> Color.Color
|
||||||
|
yellow flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.yellow
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.yellow
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.yellow
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.yellow
|
||||||
|
|
||||||
|
|
||||||
|
yellowUI : Flavor -> Element.Color
|
||||||
|
yellowUI =
|
||||||
|
yellow >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
green : Flavor -> Color.Color
|
||||||
|
green flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.green
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.green
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.green
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.green
|
||||||
|
|
||||||
|
|
||||||
|
greenUI : Flavor -> Element.Color
|
||||||
|
greenUI =
|
||||||
|
green >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
teal : Flavor -> Color.Color
|
||||||
|
teal flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.teal
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.teal
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.teal
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.teal
|
||||||
|
|
||||||
|
|
||||||
|
tealUI : Flavor -> Element.Color
|
||||||
|
tealUI =
|
||||||
|
teal >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
sky : Flavor -> Color.Color
|
||||||
|
sky flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.sky
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.sky
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.sky
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.sky
|
||||||
|
|
||||||
|
|
||||||
|
skyUI : Flavor -> Element.Color
|
||||||
|
skyUI =
|
||||||
|
sky >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
sapphire : Flavor -> Color.Color
|
||||||
|
sapphire flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.sapphire
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.sapphire
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.sapphire
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.sapphire
|
||||||
|
|
||||||
|
|
||||||
|
sapphireUI : Flavor -> Element.Color
|
||||||
|
sapphireUI =
|
||||||
|
sapphire >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
blue : Flavor -> Color.Color
|
||||||
|
blue flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.blue
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.blue
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.blue
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.blue
|
||||||
|
|
||||||
|
|
||||||
|
blueUI : Flavor -> Element.Color
|
||||||
|
blueUI =
|
||||||
|
blue >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
lavender : Flavor -> Color.Color
|
||||||
|
lavender flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.lavender
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.lavender
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.lavender
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.lavender
|
||||||
|
|
||||||
|
|
||||||
|
lavenderUI : Flavor -> Element.Color
|
||||||
|
lavenderUI =
|
||||||
|
lavender >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
text : Flavor -> Color.Color
|
||||||
|
text flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.text
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.text
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.text
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.text
|
||||||
|
|
||||||
|
|
||||||
|
textUI : Flavor -> Element.Color
|
||||||
|
textUI =
|
||||||
|
text >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
subtext1 : Flavor -> Color.Color
|
||||||
|
subtext1 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.subtext1
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.subtext1
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.subtext1
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.subtext1
|
||||||
|
|
||||||
|
|
||||||
|
subtext1UI : Flavor -> Element.Color
|
||||||
|
subtext1UI =
|
||||||
|
subtext1 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
subtext0 : Flavor -> Color.Color
|
||||||
|
subtext0 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.subtext0
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.subtext0
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.subtext0
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.subtext0
|
||||||
|
|
||||||
|
|
||||||
|
subtext0UI : Flavor -> Element.Color
|
||||||
|
subtext0UI =
|
||||||
|
subtext0 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
overlay2 : Flavor -> Color.Color
|
||||||
|
overlay2 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.overlay2
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.overlay2
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.overlay2
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.overlay2
|
||||||
|
|
||||||
|
|
||||||
|
overlay2UI : Flavor -> Element.Color
|
||||||
|
overlay2UI =
|
||||||
|
overlay2 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
overlay1 : Flavor -> Color.Color
|
||||||
|
overlay1 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.overlay1
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.overlay1
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.overlay1
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.overlay1
|
||||||
|
|
||||||
|
|
||||||
|
overlay1UI : Flavor -> Element.Color
|
||||||
|
overlay1UI =
|
||||||
|
overlay1 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
overlay0 : Flavor -> Color.Color
|
||||||
|
overlay0 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.overlay0
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.overlay0
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.overlay0
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.overlay0
|
||||||
|
|
||||||
|
|
||||||
|
overlay0UI : Flavor -> Element.Color
|
||||||
|
overlay0UI =
|
||||||
|
overlay0 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
surface2 : Flavor -> Color.Color
|
||||||
|
surface2 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.surface2
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.surface2
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.surface2
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.surface2
|
||||||
|
|
||||||
|
|
||||||
|
surface2UI : Flavor -> Element.Color
|
||||||
|
surface2UI =
|
||||||
|
surface2 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
surface1 : Flavor -> Color.Color
|
||||||
|
surface1 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.surface1
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.surface1
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.surface1
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.surface1
|
||||||
|
|
||||||
|
|
||||||
|
surface1UI : Flavor -> Element.Color
|
||||||
|
surface1UI =
|
||||||
|
surface1 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
surface0 : Flavor -> Color.Color
|
||||||
|
surface0 flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.surface0
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.surface0
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.surface0
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.surface0
|
||||||
|
|
||||||
|
|
||||||
|
surface0UI : Flavor -> Element.Color
|
||||||
|
surface0UI =
|
||||||
|
surface0 >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
base : Flavor -> Color.Color
|
||||||
|
base flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.base
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.base
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.base
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.base
|
||||||
|
|
||||||
|
|
||||||
|
baseUI : Flavor -> Element.Color
|
||||||
|
baseUI =
|
||||||
|
base >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
mantle : Flavor -> Color.Color
|
||||||
|
mantle flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.mantle
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.mantle
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.mantle
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.mantle
|
||||||
|
|
||||||
|
|
||||||
|
mantleUI : Flavor -> Element.Color
|
||||||
|
mantleUI =
|
||||||
|
mantle >> toElmUiColor
|
||||||
|
|
||||||
|
|
||||||
|
crust : Flavor -> Color.Color
|
||||||
|
crust flavor =
|
||||||
|
case flavor of
|
||||||
|
Frappe ->
|
||||||
|
CF.crust
|
||||||
|
|
||||||
|
Latte ->
|
||||||
|
CL.crust
|
||||||
|
|
||||||
|
Macchiato ->
|
||||||
|
CA.crust
|
||||||
|
|
||||||
|
Mocha ->
|
||||||
|
CO.crust
|
||||||
|
|
||||||
|
|
||||||
|
crustUI : Flavor -> Element.Color
|
||||||
|
crustUI =
|
||||||
|
crust >> toElmUiColor
|
Loading…
Reference in New Issue