Add save/restore through the URL

This commit is contained in:
Emi Simpson 2023-07-06 09:52:17 -04:00
parent 275c1e45a1
commit 1685ffc736
Signed by: Emi
GPG key ID: A12F2C2FFDC3D847
2 changed files with 114 additions and 31 deletions

View file

@ -6,6 +6,7 @@
"elm-version": "0.19.1", "elm-version": "0.19.1",
"dependencies": { "dependencies": {
"direct": { "direct": {
"chelovek0v/bbase64": "1.0.1",
"elm/browser": "1.0.2", "elm/browser": "1.0.2",
"elm/core": "1.0.5", "elm/core": "1.0.5",
"elm/html": "1.0.0", "elm/html": "1.0.0",
@ -14,13 +15,17 @@
"elm/url": "1.0.0", "elm/url": "1.0.0",
"elm-community/array-extra": "2.6.0", "elm-community/array-extra": "2.6.0",
"elm-community/html-extra": "3.4.0", "elm-community/html-extra": "3.4.0",
"elm-community/list-extra": "8.7.0",
"elm-community/maybe-extra": "5.3.0", "elm-community/maybe-extra": "5.3.0",
"elm-community/string-extra": "4.0.1" "elm-community/string-extra": "4.0.1",
"elm-toulouse/cbor": "1.1.0"
}, },
"indirect": { "indirect": {
"elm/bytes": "1.0.8",
"elm/regex": "1.0.0", "elm/regex": "1.0.0",
"elm/time": "1.0.0", "elm/time": "1.0.0",
"elm/virtual-dom": "1.0.3" "elm/virtual-dom": "1.0.3",
"elm-toulouse/float16": "1.0.1"
} }
}, },
"test-dependencies": { "test-dependencies": {

View file

@ -6,10 +6,15 @@ import Stickers exposing (Sticker, StickerContent(..), viewSticker)
import Array exposing (Array, get, set) import Array exposing (Array, get, set)
import Array.Extra as Array import Array.Extra as Array
import Base64.Encode as B64E
import Base64.Decode as B64D
import Browser import Browser
import Browser.Dom exposing (focus, getViewport, Viewport) import Browser.Dom exposing (focus, getViewport, Viewport)
import Browser.Navigation exposing (Key) import Browser.Navigation exposing (Key, pushUrl)
import Browser.Events exposing (onMouseMove, onMouseUp, onResize) import Browser.Events exposing (onMouseMove, onMouseUp, onResize)
import Cbor exposing (CborItem(..))
import Cbor.Encode as CE
import Cbor.Decode as CD
import Html exposing (Attribute, button, div, h3, input, Html, section, text) import Html exposing (Attribute, button, div, h3, input, Html, section, text)
import Html.Attributes exposing (class, disabled, id, style, value) import Html.Attributes exposing (class, disabled, id, style, value)
import Html.Attributes.Extra as Attributes exposing (attributeIf) import Html.Attributes.Extra as Attributes exposing (attributeIf)
@ -19,7 +24,10 @@ import Html.Events.Extra exposing (onEnter)
import Html.Lazy exposing (lazy, lazy2) import Html.Lazy exposing (lazy, lazy2)
import Json.Decode as D import Json.Decode as D
import List exposing (map, singleton) import List exposing (map, singleton)
import List.Extra exposing (elemIndex, getAt)
import Maybe exposing (andThen)
import Maybe.Extra exposing (isJust) import Maybe.Extra exposing (isJust)
import Result exposing (toMaybe)
import String exposing (fromInt, isEmpty) import String exposing (fromInt, isEmpty)
import String.Extra exposing (isBlank) import String.Extra exposing (isBlank)
import Svg exposing (path, Svg, svg) import Svg exposing (path, Svg, svg)
@ -52,12 +60,10 @@ type alias Model =
{ columns: Array Column { columns: Array Column
, heldTile: Maybe HeldTile , heldTile: Maybe HeldTile
, mousePos: (Int, Int) , mousePos: (Int, Int)
, browserKey: Key
} }
type alias Column = type alias Column = { tiles: Array Tile }
{ name: String
, tiles: Array Tile
}
type alias Tile = type alias Tile =
{ text: String { text: String
@ -74,25 +80,17 @@ blankTile = { text = "", stickers = [] }
init : () -> Url -> Key -> (Model, Cmd Msg) init : () -> Url -> Key -> (Model, Cmd Msg)
init flags url browserKey = init flags url browserKey =
{ columns = let
[ { name = "Into" m =
, tiles = Array.empty case load browserKey url of
} Just model -> model
, { name = "Willing" Nothing ->
, tiles = Array.empty { columns = Array.repeat 4 (Column Array.empty)
} , mousePos = (200, 200)
, { name = "Maybe" , heldTile = Nothing
, tiles = Array.empty , browserKey = browserKey
} }
, { name = "No" in m |> withoutCmd
, tiles = Array.empty
}
]
|> Array.fromList
, mousePos = (200, 200)
, heldTile = Nothing
}
|> withoutCmd
subscriptions : Model -> Sub Msg subscriptions : Model -> Sub Msg
subscriptions model = subscriptions model =
@ -140,7 +138,7 @@ viewFloatyTile (mouseX, mouseY) { tile, hoveredColumnIndex } =
viewHoverTile = div [id "hover-tile"] [] viewHoverTile = div [id "hover-tile"] []
viewColumn : Maybe HeldTile -> Int -> Column -> Html Msg viewColumn : Maybe HeldTile -> Int -> Column -> Html Msg
viewColumn heldOverTile columnIndex {name, tiles} = viewColumn heldOverTile columnIndex {tiles} =
let let
isTileHeld = isJust heldOverTile isTileHeld = isJust heldOverTile
tileCount = Array.length tiles tileCount = Array.length tiles
@ -163,11 +161,19 @@ viewColumn heldOverTile columnIndex {name, tiles} =
] ]
( ( h3 ( ( h3
[] []
[text name] [text <| columnIndexToName columnIndex]
) :: tilesList ) :: tilesList
++ [ viewAddTile isTileHeld columnIndex tileCount ] ++ [ viewAddTile isTileHeld columnIndex tileCount ]
) )
columnIndexToName : Int -> String
columnIndexToName i = case i of
0 -> "Into"
1 -> "Willing"
2 -> "Maybe"
3 -> "No"
_ -> "Too Many Columns"
viewTile : Bool -> Int -> Int -> Tile -> Html Msg viewTile : Bool -> Int -> Int -> Tile -> Html Msg
viewTile isTileHeld columnIndex tileIndex tile = viewTile isTileHeld columnIndex tileIndex tile =
div div
@ -313,7 +319,7 @@ update msg model = case Debug.log "UPDATE" msg of
(modColumnInPage columnIndex << modTileInColumn tileIndex) (modColumnInPage columnIndex << modTileInColumn tileIndex)
(updateTileText newText) (updateTileText newText)
model model
|> withoutCmd |> withSaveCmd
AddTile columnIndex -> AddTile columnIndex ->
modColumnInPage columnIndex modColumnInPage columnIndex
addTileToColumn addTileToColumn
@ -323,7 +329,7 @@ update msg model = case Debug.log "UPDATE" msg of
modColumnInPage columnIndex modColumnInPage columnIndex
(removeTileFromColumn tileIndex) (removeTileFromColumn tileIndex)
model model
|> withoutCmd |> withSaveCmd
HeldOverNewTile columnIndex tileIndex -> case model.heldTile of HeldOverNewTile columnIndex tileIndex -> case model.heldTile of
Nothing -> model |> withoutCmd Nothing -> model |> withoutCmd
Just oldHeldTileInfo -> Just oldHeldTileInfo ->
@ -374,4 +380,76 @@ update msg model = case Debug.log "UPDATE" msg of
model model
in in
{ modelWithTileInserted | heldTile = Nothing } { modelWithTileInserted | heldTile = Nothing }
|> withoutCmd |> withSaveCmd
withSaveCmd : Model -> (Model, Cmd Msg)
withSaveCmd model = (model, save model)
andSaveCmd : (Model, Cmd Msg) -> (Model, Cmd Msg)
andSaveCmd (model, cmd) = (model, Cmd.batch [cmd, save model])
save : Model -> Cmd Msg
save model =
saveModel model
|> CE.encode
|> B64E.bytes
|> B64E.encode
|> (++) "#"
|> pushUrl model.browserKey
load : Key -> Url -> Maybe Model
load key url =
url.fragment
|> andThen (B64D.decode B64D.bytes >> toMaybe)
|> andThen (CD.decode (loadModel key))
saveModel : Model -> CE.Encoder
saveModel { columns } =
Array.toList columns
|> map saveColumn
|> CE.sequence
array4 a b c d = Array.fromList [a, b, c, d]
loadModel : Key -> CD.Decoder Model
loadModel key =
CD.map4 array4 loadColumn loadColumn loadColumn loadColumn
|> CD.map Model
|> CD.map ((|>) Nothing)
|> CD.map ((|>) (0, 0))
|> CD.map ((|>) key)
saveColumn : Column -> CE.Encoder
saveColumn { tiles } = CE.list saveTile (Array.toList tiles)
loadColumn : CD.Decoder Column
loadColumn =
CD.list loadTile
|> CD.map Array.fromList
|> CD.map Column
saveTile : Tile -> CE.Encoder
saveTile { text } =
case elemIndex text activities of
Just index -> CE.int index
Nothing -> CE.string text
loadTile : CD.Decoder Tile
loadTile =
CD.any
|> CD.andThen loadTileInner
loadTileInner : CborItem -> CD.Decoder Tile
loadTileInner item =
let
word =
case item of
CborInt i ->
case getAt i activities of
Just activity -> CD.succeed activity
Nothing -> CD.fail
CborString s -> CD.succeed s
_ -> CD.fail
in
CD.map (flip Tile []) word