diff --git a/assets/src/Effect/Program.elm b/assets/src/Effect/Program.elm index 94bf49a3..2c2e25ba 100644 --- a/assets/src/Effect/Program.elm +++ b/assets/src/Effect/Program.elm @@ -1,4 +1,4 @@ -port module Effect.Program exposing (Config, Model(..), Msg(..), Program, State, debounceConfig, effectProgramKeyDowns, initialState, keyDownDecoder, maybeWithDebounce, maybeWithToken, program, runCmd, runSub, withCaching, wrapInit, wrapSubscriptions, wrapUpdate, wrapView) +module Effect.Program exposing (Config, Model(..), Msg(..), Program, State, debounceConfig, initialState, keyDownDecoder, maybeWithDebounce, maybeWithToken, program, runCmd, runSub, withCaching, wrapInit, wrapSubscriptions, wrapUpdate, wrapView) import Browser import Browser.Events @@ -28,9 +28,6 @@ import Time import Url exposing (Url) -port effectProgramKeyDowns : (Decode.Value -> a) -> Sub a - - type alias Config flags route model msg = { init : flags -> route -> ( model, Command msg ) , url : Url -> route @@ -56,7 +53,6 @@ debounceConfig debounceMsg = type Msg msg = UserMsg msg | SetupSocket String { url : String, token : Maybe String } (SelectionSet msg RootSubscription) - | SubscriptionMsg String Absinthe.Msg | KeyDown String | KeyUp String | Debounce String Debounce.Msg @@ -133,7 +129,7 @@ runCmd config state cmd = |> maybeWithToken token |> withCaching cache |> Graphql.Http.send identity - -- |> Cmd.map (Result.mapError Graphql.Http.ignoreParsedErrorData) + |> Cmd.map Graphql.Http.discardParsedErrorData |> Cmd.map (\result -> case result of @@ -141,8 +137,7 @@ runCmd config state cmd = UserMsg msg Err str -> - -- UserMsg (onError str) - NoOp + UserMsg (onError str) ) |> maybeWithDebounce state debounce @@ -150,7 +145,7 @@ runCmd config state cmd = Graphql.Http.mutationRequest url selection |> maybeWithToken token |> Graphql.Http.send identity - -- |> Cmd.map (Result.mapError Graphql.Http.ignoreParsedErrorData) + |> Cmd.map Graphql.Http.discardParsedErrorData |> Cmd.map (\result -> case result of @@ -158,8 +153,7 @@ runCmd config state cmd = UserMsg msg Err str -> - -- UserMsg (onError str) - NoOp + UserMsg (onError str) ) |> maybeWithDebounce state debounce @@ -207,19 +201,21 @@ runCmd config state cmd = keyDownDecoder : Bool -> Bool -> String -> msg -> Decoder (Msg msg) keyDownDecoder needsShift needsMeta key msg = - Decode.map3 - (\actualKey shift meta -> + Decode.andThen + (\( actualKey, shift, meta ) -> if shift == needsShift && meta == needsMeta && key == actualKey then - UserMsg msg + Decode.succeed (UserMsg msg) else - NoOp + Decode.fail "" ) - (Decode.field "key" Decode.string) - (Decode.field "shiftKey" Decode.bool) - (Decode.map2 (||) - (Decode.field "metaKey" Decode.bool) - (Decode.field "ctrlKey" Decode.bool) + (Decode.map3 (\actualKey shift meta -> ( actualKey, shift, meta )) + (Decode.field "key" Decode.string) + (Decode.field "shiftKey" Decode.bool) + (Decode.map2 (||) + (Decode.field "metaKey" Decode.bool) + (Decode.field "ctrlKey" Decode.bool) + ) ) @@ -227,15 +223,7 @@ runSub : Config flags route model msg -> State msg -> Subscription msg -> Sub (M runSub config state sub = case sub of Subscription.KeyCombo combo msg -> - effectProgramKeyDowns - (\value -> - case Decode.decodeValue (keyDownDecoder combo.shift combo.meta combo.key msg) value of - Ok wrappedMsg -> - wrappedMsg - - Err _ -> - NoOp - ) + Browser.Events.onKeyDown (keyDownDecoder combo.shift combo.meta combo.key msg) Subscription.PortReceive channel callback -> config.inbound <| @@ -285,9 +273,6 @@ runSub config state sub = Absinthe.Status bool -> UserMsg (onStatus bool) - - Absinthe.Control msg -> - SubscriptionMsg key msg ) Nothing -> @@ -364,7 +349,7 @@ wrapUpdate config msg model = SetupSocket key socketConfig selection -> let ( absinthe, absintheCmd ) = - Absinthe.init socketConfig always selection + Absinthe.init socketConfig selection in ( Running { state | sockets = Dict.insert key absinthe state.sockets } @@ -373,23 +358,6 @@ wrapUpdate config msg model = , absintheCmd ) - SubscriptionMsg key socketMsg -> - case Dict.get key state.sockets of - Just socket -> - let - ( newSocket, socketCmd ) = - Absinthe.update socketMsg socket - in - ( Running - { state | sockets = Dict.insert key newSocket state.sockets } - flags - m - , Cmd.map (SubscriptionMsg key) socketCmd - ) - - Nothing -> - ( model, Cmd.none ) - wrapView : Config flags route model msg -> Model flags model msg -> Browser.Document (Msg msg) wrapView config model = diff --git a/assets/src/Network/Absinthe/Socket.elm b/assets/src/Network/Absinthe/Socket.elm index a5d89c63..682708b9 100644 --- a/assets/src/Network/Absinthe/Socket.elm +++ b/assets/src/Network/Absinthe/Socket.elm @@ -1,13 +1,4 @@ -port module Network.Absinthe.Socket exposing - ( Info(..) - , Logger - , Msg - , Socket - , emptyLogger - , init - , listen - , update - ) +port module Network.Absinthe.Socket exposing (Info(..), Socket, init, listen) import Extra.Json.Encode as Encode import Graphql.Document as Document @@ -23,8 +14,8 @@ port absintheSocketOutbound : Value -> Cmd msg port absintheSocketInbound : (Value -> msg) -> Sub msg -socketListen : Socket -> Sub Info -socketListen state = +listen : Socket -> Sub Info +listen state = absintheSocketInbound <| \input -> case Decode.decodeValue (infoDecoder state) input of @@ -32,37 +23,20 @@ socketListen state = info Err _ -> - Control SocketClosed + Status False type alias Socket = - { connected : Bool - , url : String + { url : String , token : Maybe String - , logger : Logger , document : String } -type alias Logger = - String -> String -> String - - -emptyLogger : Logger -emptyLogger a b = - b - - -init : - { url : String, token : Maybe String } - -> Logger - -> SelectionSet a RootSubscription - -> ( Socket, Cmd msg ) -init { url, token } logger doc = - ( { connected = False - , url = url +init : { url : String, token : Maybe String } -> SelectionSet a RootSubscription -> ( Socket, Cmd msg ) +init { url, token } doc = + ( { url = url , token = token - , logger = logger , document = Document.serializeSubscription doc } , absintheSocketOutbound <| @@ -78,59 +52,6 @@ init { url, token } logger doc = type Info = Data Value | Status Bool - | Control Msg - - -type Msg - = SocketOpened - | SocketClosed - | ProblemResponse String - | NoOp - - -update : Msg -> Socket -> ( Socket, Cmd Msg ) -update msg state = - case msg of - SocketOpened -> - ( { state | connected = True } - , Cmd.none - ) - - SocketClosed -> - ( { state | connected = False } - , Cmd.none - ) - - ProblemResponse string -> - let - _ = - state.logger "Malformed response" string - in - ( state, Cmd.none ) - - NoOp -> - ( state, Cmd.none ) - - -listen : Socket -> Sub Info -listen state = - Sub.batch - [ Sub.map toStatus (socketListen state) - , socketListen state - ] - - -toStatus : Info -> Info -toStatus socketInfo = - case socketInfo of - Control SocketClosed -> - Status False - - Control SocketOpened -> - Status True - - _ -> - Control NoOp infoDecoder : Socket -> Decoder Info @@ -140,11 +61,11 @@ infoDecoder state = (\tag -> case tag of "Open" -> - Decode.succeed <| Control SocketOpened + Decode.succeed <| Status True "Data" -> Decode.map Data <| Decode.field "data" Decode.value _ -> - Decode.succeed <| Control SocketClosed + Decode.succeed <| Status False ) diff --git a/assets/src/Network/Absinthe/Socket.js b/assets/src/Network/Absinthe/Socket.js index 5cd4c60d..86e2b3ca 100644 --- a/assets/src/Network/Absinthe/Socket.js +++ b/assets/src/Network/Absinthe/Socket.js @@ -1,24 +1,41 @@ -import * as AbsintheSocket from "@absinthe/socket"; -import { Socket as PhoenixSocket } from "phoenix"; - -const createPhoenixSocket = (address, params) => - new PhoenixSocket(address, { params }); - export default { - start(app) { - const onOpen = () => { + start: app => { + const initialize = ({ url, token, doc }) => { + return Promise.all([ + import(/* webpackChunkName: "phoenix" */ "phoenix"), + import(/* webpackChunkName: "absinthe-socket" */ "@absinthe/socket") + ]).then(([Phoenix, AbsintheSocket]) => { + const phoenixSocket = new Phoenix.Socket(url, { params: { token } }); + const absintheSocket = AbsintheSocket.create(phoenixSocket); + + const notifier = AbsintheSocket.send(absintheSocket, { + operation: doc, + variables: {} + }); + + AbsintheSocket.observe(absintheSocket, notifier, { + onStart, + onAbort, + onError, + onCancel, + onResult + }); + }); + }; + + const onStart = _data => { app.ports.absintheSocketInbound.send({ tag: "Open" }); }; - const onAbort = data => { + const onAbort = _data => { app.ports.absintheSocketInbound.send({ tag: "Abort" }); }; - const onCancel = data => { + const onCancel = _data => { app.ports.absintheSocketInbound.send({ tag: "Cancel" }); }; - const onError = data => { + const onError = _data => { app.ports.absintheSocketInbound.send({ tag: "Error" }); }; @@ -29,25 +46,7 @@ export default { app.ports.absintheSocketOutbound.subscribe(data => { switch (data.tag) { case "Initialize": { - const phoenixSocket = createPhoenixSocket(data.url, { - token: data.token - }); - const absintheSocket = AbsintheSocket.create(phoenixSocket); - - phoenixSocket.onOpen(onOpen); - - const notifier = AbsintheSocket.send(absintheSocket, { - operation: data.doc, - variables: {} - }); - - AbsintheSocket.observe(absintheSocket, notifier, { - onAbort, - onError, - onCancel, - onResult - }); - + initialize(data); break; } diff --git a/assets/src/Pages/Editor/Effects.elm b/assets/src/Pages/Editor/Effects.elm index d80b26a0..82ce051b 100644 --- a/assets/src/Pages/Editor/Effects.elm +++ b/assets/src/Pages/Editor/Effects.elm @@ -36,8 +36,8 @@ import Pages.Editor.Types.WorkspaceUpdate as WorkspaceUpdate exposing (Workspace getRevision : Revision.Id -> Command (Result (Graphql.Http.Error ()) Revision) getRevision revisionId = let - query = - SelectionSet.succeed identity + selection = + SelectionSet.succeed Ok |> SelectionSet.with (ApiQuery.revision arguments revisionQuery) arguments = @@ -54,7 +54,7 @@ getRevision revisionId = Command.GraphqlQuery { url = "/api" , token = Nothing - , selection = SelectionSet.map Ok query + , selection = selection , onError = Err , debounce = Nothing , cache = Command.Permanent @@ -64,8 +64,8 @@ getRevision revisionId = searchPackages : String -> Command (Result (Graphql.Http.Error ()) (List Package)) searchPackages queryString = let - query = - SelectionSet.succeed identity + selection = + SelectionSet.succeed Ok |> SelectionSet.with (ApiQuery.packageSearch arguments packageQuery) arguments = @@ -79,7 +79,7 @@ searchPackages queryString = Command.GraphqlQuery { url = "/api" , token = Nothing - , selection = SelectionSet.map Ok query + , selection = selection , onError = Err , debounce = Just "package-search" , cache = Command.Temporary @@ -89,8 +89,8 @@ searchPackages queryString = formatCode : Jwt -> Version -> String -> Command (Result (Graphql.Http.Error ()) String) formatCode token version code = let - mutation = - SelectionSet.succeed identity + selection = + SelectionSet.succeed Ok |> SelectionSet.with (ApiMutation.formatCode arguments) arguments = @@ -101,7 +101,7 @@ formatCode token version code = Command.GraphqlMutation { url = "/api" , token = Just token - , selection = SelectionSet.map Ok mutation + , selection = selection , onError = Err , debounce = Just "format-code" } @@ -111,7 +111,7 @@ compile : Jwt -> Version -> String -> List Package -> Command (Result (Graphql.H compile token elmVersion elmCode packages = let mutation = - SelectionSet.succeed (\_ -> ()) + SelectionSet.succeed (\_ -> Ok ()) |> SelectionSet.with (ApiMutation.compile arguments) arguments = @@ -128,7 +128,7 @@ compile token elmVersion elmCode packages = Command.GraphqlMutation { url = "/api" , token = Just token - , selection = SelectionSet.map Ok mutation + , selection = mutation , onError = Err , debounce = Nothing } @@ -183,7 +183,7 @@ attachToWorkspace : Jwt -> Version -> Command (Result (Graphql.Http.Error ()) () attachToWorkspace token version = let selection = - SelectionSet.succeed (\_ -> ()) + SelectionSet.succeed (\_ -> Ok ()) |> SelectionSet.with (ApiMutation.attachToWorkspace arguments) arguments = @@ -192,7 +192,7 @@ attachToWorkspace token version = Command.GraphqlMutation { url = "/api" , token = Just token - , selection = SelectionSet.map Ok selection + , selection = selection , onError = Err , debounce = Nothing } @@ -211,7 +211,7 @@ createRevision : Jwt -> Int -> Revision -> Command (Result (Graphql.Http.Error ( createRevision token termsVersion revision = let selection = - SelectionSet.succeed identity + SelectionSet.succeed Ok |> SelectionSet.with (ApiMutation.createRevision arguments revisionSelection) arguments = @@ -237,7 +237,7 @@ createRevision token termsVersion revision = Command.GraphqlMutation { url = "/api" , token = Just token - , selection = SelectionSet.map Ok selection + , selection = selection , onError = Err , debounce = Nothing } @@ -268,8 +268,8 @@ getDocs packages = Command.GraphqlQuery { url = "/api" , token = Nothing - , selection = SelectionSet.map identity selection - , onError = always [] + , selection = selection + , onError = \_ -> [] , debounce = Nothing , cache = Command.Permanent } diff --git a/assets/src/Pages/Editor/State/App.elm b/assets/src/Pages/Editor/State/App.elm index 9bcbe5a0..e736c16b 100644 --- a/assets/src/Pages/Editor/State/App.elm +++ b/assets/src/Pages/Editor/State/App.elm @@ -67,7 +67,7 @@ update flags msg_ model = Initial initFlags _ -> init initFlags route - Setup setupState -> + Setup _ -> update flags (SetupMsg (Setup.RouteChanged route)) model _ -> diff --git a/assets/src/Pages/Editor/State/Working.elm b/assets/src/Pages/Editor/State/Working.elm index a64d67e5..1a07c5d3 100644 --- a/assets/src/Pages/Editor/State/Working.elm +++ b/assets/src/Pages/Editor/State/Working.elm @@ -870,6 +870,7 @@ subscriptions model = else OnlineStatusChanged False ) + , Subscription.map EditorActionPerformed Effects.keyCombos ] diff --git a/assets/src/Pages/Embed/index.js b/assets/src/Pages/Embed/index.js index 8540b337..c5352109 100644 --- a/assets/src/Pages/Embed/index.js +++ b/assets/src/Pages/Embed/index.js @@ -1,8 +1,8 @@ +import NetworkAbsintheSocket from "../../Network/Absinthe/Socket"; import EllieUiIcon from "../../Ellie/Ui/Icon"; import EllieUiCodeEditor from "../../Ellie/Ui/CodeEditor"; import EllieUiOutput from "../../Ellie/Ui/Output"; import EllieConstants from "../../Ellie/Constants"; -import NetworkAbsintheSocket from "../../Network/Absinthe/Socket"; import PagesEmbedMain from "./Main"; import "../../Ellie/Ui/CodeEditor.css"; import "../../Ellie/Ui/Logo.css"; diff --git a/config/dev.exs b/config/dev.exs index bd406006..86d1630d 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -14,23 +14,13 @@ config :ellie, EllieWeb.Endpoint, code_reloader: true, check_origin: false, watchers: [ - node: [ - "node_modules/webpack-dev-server/bin/webpack-dev-server.js", - "--mode", - "development", - "--watch-stdin", - "--config", - "./webpack.dev.config.js", + npm: [ + "start", cd: Path.expand("../assets", __DIR__) ], - node: [ - "node_modules/@dillonkearns/elm-graphql/bin/elm-graphql", - "--introspection-file", - "../priv/graphql/schema.json", - "--base", - "Ellie.Api", - "--output", - "elm-stuff/generated/dillonkearns/graphql", + npm: [ + "run", + "graphql", cd: Path.expand("../assets", __DIR__) ] ] diff --git a/lib/elm/platform/local_18.ex b/lib/elm/platform/local_18.ex index 4ded80d9..6666f213 100644 --- a/lib/elm/platform/local_18.ex +++ b/lib/elm/platform/local_18.ex @@ -68,7 +68,7 @@ defmodule Elm.Platform.Local18 do @spec format(String.t()) :: {:ok, String.t()} | :error def format(code) do - binary = Path.join(base_path, "elm-format") + binary = Path.join(base_path(), "elm-format") args = ["--stdin"] options = [in: code, out: :string, err: :string] result = Porcelain.exec(binary, args, options) @@ -93,7 +93,7 @@ defmodule Elm.Platform.Local18 do end def handle_call({:elm_package, :init, root}, _from, state) do - binary = Path.join(base_path, "elm-package") + binary = Path.join(base_path(), "elm-package") args = ["--num", "1", binary, "install", "--yes"] options = [out: :string, err: :string, dir: root] result = Porcelain.exec("sysconfcpus", args, options) @@ -108,7 +108,7 @@ defmodule Elm.Platform.Local18 do end def handle_call({:elm_make, root, entry, output}, _from, state) do - binary = Path.join(base_path, "elm-make") + binary = Path.join(base_path(), "elm-make") args = [ "--num", diff --git a/lib/elm/platform/local_19.ex b/lib/elm/platform/local_19.ex index d5c3afb5..317e8281 100644 --- a/lib/elm/platform/local_19.ex +++ b/lib/elm/platform/local_19.ex @@ -61,7 +61,7 @@ defmodule Elm.Platform.Local19 do File.mkdir_p!(Path.join(root, Path.dirname(entry))) File.write!(Path.join(root, entry), source) - binary = Path.join(base_path, "elm") + binary = Path.join(base_path(), "elm") args = [ "--num", @@ -101,7 +101,7 @@ defmodule Elm.Platform.Local19 do @spec format(String.t()) :: {:ok, String.t()} | :error def format(code) do - binary = Path.join(base_path, "elm-format") + binary = Path.join(base_path(), "elm-format") args = ["--stdin"] options = [in: code, out: :string, err: :string] result = Porcelain.exec(binary, args, options) @@ -118,7 +118,7 @@ defmodule Elm.Platform.Local19 do # Helpers defp install_transitive_deps(root) do - binary = Path.join(base_path, "elm") + binary = Path.join(base_path(), "elm") args = ["--num", "1", binary, "make", "--report", "json"] options = [dir: root, out: :string, err: :string] result = Porcelain.exec("sysconfcpus", args, options) @@ -236,7 +236,7 @@ defmodule Elm.Platform.Local19 do end defp elm_init(root) do - binary = Path.join(base_path, "elm") + binary = Path.join(base_path(), "elm") args = ["init"] options = [out: :string, err: :string, dir: root, in: "Y"] result = Porcelain.exec(binary, args, options)