elm-format

main
Bram 2024-11-03 21:09:37 +01:00
parent 85790d4e7b
commit 7711ce8c0d
9 changed files with 359 additions and 155 deletions

View File

@ -1,43 +1,58 @@
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 Element.Events
import Html.Attributes
import Layout
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
}
-> 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
}
)
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
}
)

View File

@ -1,28 +1,35 @@
module Items.Introduction exposing (..)
import Color exposing (Color)
import Element exposing (Element)
import Element.Background
import Widget.Material.Typography
import Theme
import Widget.Material.Typography
-- MODEL
type alias Model = ()
type alias Msg = ()
type alias Model =
()
type alias Msg =
()
-- UPDATE
-- SUBSCRIPTIONS
-- VIEW
view :
{ colorBackground : Color
, width : Int
} -> Element msg
}
-> 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."
@ -35,10 +42,14 @@ view data =
, Element.width (Element.px data.width)
]
header : String -> Element msg
header =
Element.text >> Element.el Widget.Material.Typography.h1
>> List.singleton >> Element.paragraph []
Element.text
>> Element.el Widget.Material.Typography.h1
>> List.singleton
>> Element.paragraph []
text : String -> Element msg
text =

View File

@ -1,48 +1,65 @@
module Items.ItemPicker exposing (..)
import Iddict exposing (Iddict)
import Color exposing (Color)
import Element exposing (Element)
import Iddict exposing (Iddict)
import Layout
import Material.Icons
import Color exposing (Color)
-- MODEL
type alias Model a =
{ hover : Maybe Int
, items : Iddict a
}
type Msg = OnHover Int | OnHoverOut Int
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
extract =
.items
{-| Display the item picker. Note that the item should not be wider than 360px.
-}
@ -56,7 +73,8 @@ view :
, onAddNew : Maybe msg
, onClick : Int -> msg
, width : Int
} -> Element msg
}
-> Element msg
view data =
Layout.sideList
{ color = data.colorMenu
@ -75,21 +93,21 @@ view data =
}
)
|> (\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"
}
]
)
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
}

View File

@ -1,4 +1,5 @@
module Items.LoginView 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.
-}
@ -7,15 +8,18 @@ import Color exposing (Color)
import Element exposing (Element)
import Element.Background
import Element.Border
import Items.FlavorPicker as FlavorPicker
import Layout
import Material.Icons
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
@ -23,16 +27,19 @@ type alias Model =
, username : String
}
type Msg
= SetAccessToken String
| SetPassword String
| SetUsername String
| SwitchMethod LoginMethod
type LoginMethod
= AccessToken
| Password
init : Model
init =
{ accessToken = ""
@ -41,8 +48,11 @@ init =
, username = ""
}
-- UPDATE
update : Msg -> Model -> Model
update msg model =
case msg of
@ -58,8 +68,11 @@ update msg model =
SwitchMethod method ->
{ model | loginMethod = method }
-- VIEW
view :
{ colorBackground : Color
, colorMain : Color
@ -73,7 +86,8 @@ view :
, onSubmit : Matrix.Vault -> msg
, toMsg : Msg -> msg
, width : Int
} -> Element msg
}
-> Element msg
view data =
[ viewLoginMethodPicker
{ color = data.colorMain
@ -97,7 +111,7 @@ view data =
, show = False
, text = data.model.accessToken
}
Password ->
Layout.passwordInput
{ color = data.colorTextField
@ -108,17 +122,17 @@ view data =
, 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 ]
{ 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)
@ -133,7 +147,7 @@ view data =
|> Element.el
[ Element.Background.color (Theme.toElmUiColor data.colorBackground)
, Element.inFront
( FlavorPicker.view
(FlavorPicker.view
{ height = 30
, flavor = data.flavor
, onClick = data.onFlavorPick
@ -147,6 +161,7 @@ view data =
, Element.width (Element.px data.width)
]
toVault : Model -> Maybe Matrix.Vault
toVault model =
case model.loginMethod of
@ -168,6 +183,7 @@ toVault model =
|> Matrix.fromUserId
|> Maybe.map (Matrix.Settings.setPassword model.password)
viewLoginMethodPicker : { color : Color, loginMethod : LoginMethod, toMsg : LoginMethod -> msg } -> Element msg
viewLoginMethodPicker data =
Layout.tab
@ -175,24 +191,24 @@ viewLoginMethodPicker data =
, content = always Element.none
, items =
[ { icon = Layout.iconAsIcon Material.Icons.key
, text = "Access token"
, 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
\i ->
if i == 0 then
data.toMsg AccessToken
else
data.toMsg Password
, selected =
case data.loginMethod of
AccessToken ->
0
Password ->
1
}

View File

@ -1,63 +1,83 @@
module Items.VaultList exposing (..)
{-| # Vault list
{-|
# 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 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
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
| 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

View File

@ -1,9 +1,12 @@
module Layout exposing
( tab, twoBlocks
( twoBlocks
, tab, sideIconBar
, iconAsElement, iconAsIcon
, containedButton, outlinedButton, textButton, sideList
, containedButton, outlinedButton, textButton
, textInput, passwordInput
, loadingIndicator, itemWithSubtext
, itemWithSubtext
, sideList
, loadingIndicator
)
{-|
@ -19,14 +22,17 @@ beautiful Material design Elm webpage.
@docs twoBlocks
## Elements
@docs tab
@docs tab, sideIconBar
## Icons
@docs iconAsElement, iconAsIcon
## Buttons
@docs containedButton, outlinedButton, textButton
@ -36,14 +42,17 @@ beautiful Material design Elm webpage.
@docs textInput, passwordInput
## Items in a list
@docs itemWithSubtext
## Lists
@docs sideList
## Other elements
@docs loadingIndicator
@ -52,12 +61,13 @@ beautiful Material design Elm webpage.
import Color exposing (Color)
import Element exposing (Element)
import Element.Events
import Element.Input
import Material.Icons.Types
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.
@ -79,12 +89,14 @@ containedButton data =
)
{ text = data.text, icon = data.icon, onPress = data.onPress }
iconAsElement :
{ color : Color
, height : Int
, icon : Material.Icons.Types.Icon msg
, width : Int
} -> Element msg
}
-> Element msg
iconAsElement data =
data.icon
|> iconAsIcon
@ -95,10 +107,12 @@ iconAsElement data =
, 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 :
@ -109,10 +123,10 @@ itemWithSubtext :
, text : String
, title : String
}
-> Widget.Item msg
-> Widget.Item msg
itemWithSubtext data =
Widget.multiLineItem
( { primary = data.color, onPrimary = data.color }
({ primary = data.color, onPrimary = data.color }
|> singlePalette
|> Material.multiLineItem
)
@ -123,20 +137,22 @@ itemWithSubtext data =
, text = data.text
}
{-| Circular loading bar indicator
-}
loadingIndicator :
{ color : Color
}
-> Element msg
-> Element msg
loadingIndicator data =
Widget.circularProgressIndicator
( { primary = data.color, onPrimary = data.color }
({ primary = data.color, onPrimary = data.color }
|> singlePalette
|> Material.progressIndicator
)
Nothing
{-| An outlined button representing an important action within a group.
-}
outlinedButton :
@ -154,6 +170,7 @@ outlinedButton data =
)
{ text = data.text, icon = data.icon, onPress = data.onPress }
{-| Show a password field
-}
passwordInput :
@ -201,13 +218,51 @@ singlePalette { primary, onPrimary } =
}
}
sideList : { color : Color, items : List (Widget.Item msg), width : Int }-> 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)
iconSize =
data.width // 2
in
data.items
|> List.map
(\item ->
[ item.icon { size = iconSize, color = data.colorText }
, Element.paragraph [] [ Element.text item.text ]
]
|> Element.column [ Element.centerX, Element.centerY ]
|> Element.el
[ Element.Events.onClick item.onPress
, Element.height (Element.px buttonHeight)
, Element.width (Element.px data.width)
]
)
|> Element.column
[ 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 =
let
width px = Element.width (Element.px px)
width px =
Element.width (Element.px px)
in
Widget.itemList
( { primary = data.color, onPrimary = data.color }
({ primary = data.color, onPrimary = data.color }
|> singlePalette
|> Material.sideSheet
)
@ -215,6 +270,7 @@ sideList data =
|> Element.el [ Element.centerX, width (Basics.min 360 data.width) ]
|> Element.el [ width data.width ]
{-| A tab selector that always has an item selected.
-}
tab :
@ -285,6 +341,7 @@ textInput data =
, onChange = data.onChange
}
{-| Two blocks either next to each other or below each other, depending on the
screen shape.
-}
@ -293,18 +350,38 @@ twoBlocks :
, el1 : { height : Int, width : Int } -> Element msg
, el2 : { height : Int, width : Int } -> Element msg
, width : Int
} -> Element msg
}
-> 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
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 }
]
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 }
]

View File

@ -5,14 +5,14 @@ import Browser.Dom
import Browser.Events
import Element exposing (Element)
import Element.Background
import Element.Font
import Items.VaultList as VaultList
import Matrix
import Recursion
import Screen.Vault as VaultScreen
import Screen.Welcome as WelcomeScreen
import Task
import Theme
import Items.VaultList as VaultList
import Screen.Welcome as WelcomeScreen
import Screen.Vault as VaultScreen
import Element.Font
import Recursion
import Matrix
main : Program () Model Msg
@ -36,10 +36,12 @@ type alias Model =
, width : Int
}
type Screen
= ScreenWelcome WelcomeScreen.Model
| ScreenVault Vaults Int VaultScreen.Model
type Msg
= OnScreenVault Int VaultScreen.Msg
| OnScreenWelcome WelcomeScreen.Msg
@ -49,7 +51,9 @@ type Msg
| ScreenSize { height : Int, width : Int }
| SetFlavor Theme.Flavor
type alias Vaults = VaultList.Model
type alias Vaults =
VaultList.Model
init : () -> ( Model, Cmd Msg )
@ -86,12 +90,13 @@ update msg model =
( { model | screen = ScreenVault welcomeMdl i newMdl }
, Cmd.none
)
else
( model, Cmd.none )
_ ->
( model, Cmd.none )
OnScreenWelcome m ->
case model.screen of
ScreenWelcome mdl ->
@ -100,34 +105,35 @@ update msg model =
( { model | screen = ScreenWelcome newMdl }
, Cmd.none
)
_ ->
( model, Cmd.none )
OnSelectVault vaults i ->
( { model
| screen =
| 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 =
| 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
}
@ -141,7 +147,7 @@ update msg model =
( { model | height = height, width = width }
, Cmd.none
)
SetFlavor flavor ->
( { model | flavor = flavor }, Cmd.none )
@ -174,15 +180,27 @@ view model =
|> List.singleton
}
viewScreen : Model -> Element Msg
viewScreen model =
let
colorBackground = Theme.base model.flavor
colorBackground2 = Theme.mantle model.flavor
colorMain = Theme.mauve model.flavor
colorSurface0 = Theme.surface0 model.flavor
colorSurface1 = Theme.surface1 model.flavor
colorText = Theme.text model.flavor
colorBackground =
Theme.base model.flavor
colorBackground2 =
Theme.mantle model.flavor
colorMain =
Theme.mauve model.flavor
colorSurface0 =
Theme.surface0 model.flavor
colorSurface1 =
Theme.surface1 model.flavor
colorText =
Theme.text model.flavor
in
Recursion.runRecursion
(\screen ->
@ -201,7 +219,7 @@ viewScreen model =
, width = model.width
}
|> Recursion.base
Nothing ->
WelcomeScreen.fromVaults vaults
|> ScreenWelcome
@ -226,4 +244,3 @@ viewScreen model =
|> Recursion.base
)
model.screen

View File

@ -4,25 +4,39 @@ import Color exposing (Color)
import Element exposing (Element)
import Matrix
-- MODEL
type alias Model = ()
type alias Msg = ()
type alias Model =
()
type alias Msg =
()
init : Model
init = ()
init =
()
-- UPDATE
update : Msg -> Model -> Model
update msg model =
case msg of
() ->
model
-- VIEW
view :
{ colorBackground : Color
, colorText : Color
@ -32,7 +46,7 @@ view :
, toMsg : Msg -> msg
, vault : Matrix.Vault
, width : Int
} -> Element msg
}
-> Element msg
view data =
Element.none

View File

@ -1,23 +1,27 @@
module Screen.Welcome exposing (..)
import Color exposing (Color)
import Element exposing (Element)
import Items.Introduction as Introduction
import Items.ItemPicker as ItemPicker
import Items.VaultList as VaultList
import Matrix
import Items.LoginView as LoginView
import Element exposing (Element)
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
@ -25,20 +29,25 @@ type Msg
| 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
@ -47,24 +56,28 @@ update msg model =
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
@ -79,10 +92,12 @@ view :
, onSelectVault : VaultList.Model -> Int -> msg
, toMsg : Msg -> msg
, width : Int
} -> Element msg
}
-> Element msg
view data =
let
onSelectVault = data.onSelectVault (ItemPicker.extract data.model.vaults)
onSelectVault =
data.onSelectVault (ItemPicker.extract data.model.vaults)
in
case data.model.login of
Just login ->
@ -141,9 +156,10 @@ viewIntroduction :
, onAddNew : Maybe msg
, onSelectVault : Int -> msg
, width : Int
} -> Element msg
}
-> Element msg
viewIntroduction data =
[ Introduction.view { colorBackground = data.colorBackground, width = data.width }
[ Introduction.view { colorBackground = data.colorBackground, width = data.width }
, ItemPicker.view
{ colorMenu = data.colorMenu
, colorText = data.colorText