From 5222ecd9b1e8c6fbd96be4e5938d96a7d2f2ff79 Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 28 Jan 2020 02:25:22 +0000 Subject: [PATCH 1/3] Resolve "Cardano Explorer Link" --- CHANGELOG.md | 12 ++++++ src/OxHead.elm | 109 +++++++++++++++++++++++++++++-------------------- 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59579de..3f6b61c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [0.23.0] - 2020-01-27 + +### Added + +- Link to Selected Stake Pool on Cardano Explorer + +## [0.22.1] - 2020-01-26 + +### Changed + +- Ratio Numerator/Denominator to Percentage + ## [0.22.0] - 2020-01-20 ### Added diff --git a/src/OxHead.elm b/src/OxHead.elm index 2129b95..ff90bcd 100644 --- a/src/OxHead.elm +++ b/src/OxHead.elm @@ -372,7 +372,7 @@ type alias NodeSettingsFees = type alias NodeSettingsRewardParams = - { compoundingRatio : NodeSettingsRatio + { compoundingRatio : Ratio , compoundingType : String , epochRate : Int , epochStart : Int @@ -382,13 +382,7 @@ type alias NodeSettingsRewardParams = type alias NodeSettingsTreasuryTax = { fixed : Int - , ratio : NodeSettingsRatio - } - - -type alias NodeSettingsRatio = - { denominator : Int - , numerator : Int + , ratio : Ratio } @@ -419,7 +413,7 @@ decodeNodeSettingsFees = decodeNodeSettingsRewardParams : Json.Decode.Decoder NodeSettingsRewardParams decodeNodeSettingsRewardParams = Json.Decode.succeed NodeSettingsRewardParams - |> Json.Decode.Pipeline.required "compoundingRatio" decodeNodeSettingsRatio + |> Json.Decode.Pipeline.required "compoundingRatio" decodeRatio |> Json.Decode.Pipeline.required "compoundingType" Json.Decode.string |> Json.Decode.Pipeline.required "epochRate" Json.Decode.int |> Json.Decode.Pipeline.required "epochStart" Json.Decode.int @@ -430,12 +424,12 @@ decodeNodeSettingsTreasuryTax : Json.Decode.Decoder NodeSettingsTreasuryTax decodeNodeSettingsTreasuryTax = Json.Decode.succeed NodeSettingsTreasuryTax |> Json.Decode.Pipeline.required "fixed" Json.Decode.int - |> Json.Decode.Pipeline.required "ratio" decodeNodeSettingsRatio + |> Json.Decode.Pipeline.required "ratio" decodeRatio -decodeNodeSettingsRatio : Json.Decode.Decoder NodeSettingsRatio -decodeNodeSettingsRatio = - Json.Decode.succeed NodeSettingsRatio +decodeRatio : Json.Decode.Decoder Ratio +decodeRatio = + Json.Decode.succeed Ratio |> Json.Decode.Pipeline.required "denominator" Json.Decode.int |> Json.Decode.Pipeline.required "numerator" Json.Decode.int @@ -502,7 +496,11 @@ type alias StakePool = } -type alias StakePoolTaxRatio = + +-- Ratio is a common type across various responses + + +type alias Ratio = { numerator : Int , denominator : Int } @@ -510,7 +508,7 @@ type alias StakePoolTaxRatio = type alias StakePoolTax = { fixed : Int - , ratio : StakePoolTaxRatio + , ratio : Ratio , max : Int } @@ -532,18 +530,11 @@ decodeStakePool = |> Json.Decode.Pipeline.required "vrf_public_key" Json.Decode.string -decodeStakePoolTaxRatio : Json.Decode.Decoder StakePoolTaxRatio -decodeStakePoolTaxRatio = - Json.Decode.succeed StakePoolTaxRatio - |> Json.Decode.Pipeline.required "numerator" Json.Decode.int - |> Json.Decode.Pipeline.required "denominator" Json.Decode.int - - decodeStakePoolTax : Json.Decode.Decoder StakePoolTax decodeStakePoolTax = Json.Decode.succeed StakePoolTax |> Json.Decode.Pipeline.required "fixed" Json.Decode.int - |> Json.Decode.Pipeline.required "ratio" decodeStakePoolTaxRatio + |> Json.Decode.Pipeline.required "ratio" decodeRatio |> Json.Decode.Pipeline.optional "max" Json.Decode.int 0 @@ -1908,6 +1899,18 @@ bottomNavElements model = ] +ratioToText : Ratio -> Element Msg +ratioToText ratio = + let + calculateRatio = + toFloat ratio.denominator / toFloat ratio.numerator + + ratioString = + String.fromFloat <| calculateRatio * 100 + in + text ratioString + + viewPanel : List (Element Msg) -> Element Msg viewPanel viewContent = column @@ -2119,9 +2122,9 @@ viewNodeSettingsFees nodeSettingsFees = viewNodeSettingsRewardParams : NodeSettingsRewardParams -> List (Element Msg) viewNodeSettingsRewardParams nodeSettingsRewardParams = - [ column [ width fill ] + [ row [ width fill ] [ withTooltip "Speed at which reward is reduced. Expressed as numerator/denominator" <| el [ alignLeft ] <| text <| "Compounding Ratio" - , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsRatio nodeSettingsRewardParams.compoundingRatio "Speed at which reward is reduced. Expressed as numerator/denominator") + , row [ alignRight ] [ ratioToText nodeSettingsRewardParams.compoundingRatio, text "%" ] ] , row [ width fill ] [ withTooltip "Reward reduction algorithm" <| el [ alignLeft ] <| text <| "Compounding Type" @@ -2148,19 +2151,9 @@ viewNodeSettingsTreasuryTax nodeSettingsTreasuryTax = [ withTooltip "What get subtracted as fixed value" <| el [ alignLeft ] <| text <| "Fixed Value" , row [ alignRight, spacing 5 ] [ adaIcon, el [ alignRight ] <| text <| String.fromInt nodeSettingsTreasuryTax.fixed ] ] - , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsRatio nodeSettingsTreasuryTax.ratio "Ratio of tax after fixed amount is subtracted. Expressed as numerator/denominator") - ] - - -viewNodeSettingsRatio : NodeSettingsRatio -> String -> List (Element Msg) -viewNodeSettingsRatio nodeSettingsRatio tooltipText = - [ row [ width fill ] - [ withTooltip tooltipText <| el [ alignLeft ] <| text <| "Denominator" - , el [ alignRight ] <| text <| String.fromInt nodeSettingsRatio.denominator - ] , row [ width fill ] - [ withTooltip tooltipText <| el [ alignLeft ] <| text <| "Numerator" - , el [ alignRight ] <| text <| String.fromInt nodeSettingsRatio.numerator + [ withTooltip "Ratio of tax after fixed amount is subtracted" <| el [ alignLeft ] <| text <| "Ratio" + , row [ alignRight ] [ ratioToText nodeSettingsTreasuryTax.ratio, text "%" ] ] ] @@ -2238,6 +2231,14 @@ viewStakeDistribution stakeDistribution stakePoolDetail stakePoolID myStakePoolI totalPools = List.length stakeDetail.pools + + cardanoExplorerLink = + if String.length stakePoolID == 64 then + newTabLink [ Font.color blue, fontMono ] + { url = "https://shelleyexplorer.cardano.org/en/stake-pool/" ++ stakePoolID, label = text "See on Explorer" } + + else + withTooltip "Invalid Stake Pool ID Length" <| el [ width fill ] <| text "See on Explorer" in [ column [ width fill, spacing 10 ] [ row [ Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, width fill, paddingXY 2 10 ] @@ -2280,10 +2281,26 @@ viewStakeDistribution stakeDistribution stakePoolDetail stakePoolID myStakePoolI ] , column [ Border.widthEach { bottom = 0, left = 0, right = 0, top = 1 }, width fill, paddingEach { bottom = 0, left = 0, right = 0, top = 10 }, spacing 10 ] [ if showSuggestions then - Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput, inputSuggestions stakePoolID (List.map (\pool -> pool.id) stakeDistribution.stake.pools) StakePoolIdClicked ] { onChange = StakePoolIdChanged, text = stakePoolID, placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")), label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") } + row [ width fill, spacing 5 ] + [ Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput, inputSuggestions stakePoolID (List.map (\pool -> pool.id) stakeDistribution.stake.pools) StakePoolIdClicked ] + { onChange = StakePoolIdChanged + , text = stakePoolID + , placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")) + , label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") + } + , cardanoExplorerLink + ] else - Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput ] { onChange = StakePoolIdChanged, text = stakePoolID, placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")), label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") } + row [ width fill, spacing 5 ] + [ Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput ] + { onChange = StakePoolIdChanged + , text = stakePoolID + , placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")) + , label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") + } + , cardanoExplorerLink + ] , case stakePoolDetail of Just spDetail -> viewStakePoolDetail spDetail @@ -2378,9 +2395,9 @@ viewStakePoolTax stakePoolTax = [ withTooltip "What get subtracted as fixed value" <| el [ alignLeft ] <| text <| "Fixed" , row [ spacing 5, alignRight ] [ adaIcon, Element.text <| formatAdaValue stakePoolTax.fixed ] ] - , column [ width fill ] - [ withTooltip "Ratio of tax after fixed amount is subtracted. Expressed as numerator/denominator" <| el [ alignLeft ] <| text <| "Ratio" - , viewStakePoolTaxRatio stakePoolTax.ratio + , wrappedRow [ width fill ] + [ withTooltip "Ratio of tax after fixed amount is subtracted" <| el [ alignLeft ] <| text <| "Ratio" + , row [ alignRight ] [ ratioToText stakePoolTax.ratio, text "%" ] ] , wrappedRow [ width fill ] [ withTooltip "Limit of tax" <| el [ alignLeft ] <| text <| "Max" @@ -2389,7 +2406,7 @@ viewStakePoolTax stakePoolTax = ] -viewStakePoolTaxRatio : StakePoolTaxRatio -> Element Msg +viewStakePoolTaxRatio : Ratio -> Element Msg viewStakePoolTaxRatio stakePoolTaxRatio = column [ width fill, spacing 10, padding 10 ] [ wrappedRow [ width fill ] @@ -2470,7 +2487,11 @@ viewStakePoolsTable stakePoolsList showZeroStaked sortAscending myStakePoolID st toFloat stakePool.value * 100 / toFloat stakedValue poolSizeString = - if poolSize > 0.01 then (String.left 4 <| String.fromFloat <| poolSize) ++ "%" else "0.00%" + if poolSize > 0.01 then + (String.left 4 <| String.fromFloat <| poolSize) ++ "%" + + else + "0.00%" poolSizeStyle = if poolSize >= 1 then -- GitLab From f997270063f55f79cac1e658a7ad0a0249aa72ca Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 28 Jan 2020 21:14:53 +0000 Subject: [PATCH 2/3] Resolve "Support Node Stats at Bootstraping" --- CHANGELOG.md | 6 ++++ src/OxHead.elm | 79 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6b61c..7d8c277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.24.0] - 2020-01-28 + +### Added + +- Support for differents Node Status + ## [0.23.0] - 2020-01-27 ### Added diff --git a/src/OxHead.elm b/src/OxHead.elm index ff90bcd..9eb8869 100644 --- a/src/OxHead.elm +++ b/src/OxHead.elm @@ -302,7 +302,18 @@ getNetworkStatsList model = -- NodeStats -type alias NodeStats = +type NodeStats + = Initializing BootstrappingNodeStats + | Running RunningNodeStats + + +type alias BootstrappingNodeStats = + { version : String + , state : String + } + + +type alias RunningNodeStats = { version : String , state : String , blockRecvCnt : Int @@ -319,11 +330,18 @@ type alias NodeStats = } -decodeNodeStats : Json.Decode.Decoder NodeStats -decodeNodeStats = - Json.Decode.succeed NodeStats +decodeBootstrappingNodeStats : Json.Decode.Decoder BootstrappingNodeStats +decodeBootstrappingNodeStats = + Json.Decode.succeed BootstrappingNodeStats + |> Json.Decode.Pipeline.required "version" Json.Decode.string + |> Json.Decode.Pipeline.required "state" Json.Decode.string + + +decodeRunningNodeStats : Json.Decode.Decoder RunningNodeStats +decodeRunningNodeStats = + Json.Decode.succeed RunningNodeStats |> Json.Decode.Pipeline.required "version" Json.Decode.string - |> Json.Decode.Pipeline.optional "state" Json.Decode.string "null" + |> Json.Decode.Pipeline.required "state" Json.Decode.string |> Json.Decode.Pipeline.required "blockRecvCnt" Json.Decode.int |> Json.Decode.Pipeline.required "lastBlockContentSize" Json.Decode.int |> Json.Decode.Pipeline.required "lastBlockDate" Json.Decode.string @@ -341,7 +359,7 @@ getNodeStats : Model -> Cmd Msg getNodeStats model = Http.get { url = model.nodeUrl ++ "/api/v0/node/stats" - , expect = expectJson GotNodeStatsMessage decodeNodeStats + , expect = expectJson GotNodeStatsMessage (Json.Decode.oneOf [ Json.Decode.map Initializing decodeBootstrappingNodeStats, Json.Decode.map Running decodeRunningNodeStats ]) } @@ -1664,16 +1682,43 @@ viewNode model = [ wrappedRow [ width fill, spacing 10 ] [ case model.nodeStats of - Just stats -> - column - [ Border.color (Element.rgb255 198 205 214) - , Border.width 2 - , padding 10 - , spacing 5 - , alignTop - , width fill - ] - (viewNodeStats stats model.showNodeStats model.zone) + Just nodeStats -> + case nodeStats of + Initializing stats -> + column + [ Border.color (Element.rgb255 198 205 214) + , Border.width 2 + , padding 10 + , spacing 5 + , alignTop + , width fill + ] + [ row [ width fill, Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, paddingXY 2 10 ] + [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Node Status" + ] + , column + [ width fill, spacing 10, padding 10 ] + [ wrappedRow [ width fill ] + [ el [ alignLeft ] (withTooltip "Node app version" <| text <| "Version") + , el [ alignRight ] <| text <| stats.version + ] + , wrappedRow [ width fill ] + [ el [ alignLeft ] (withTooltip "State of the node" <| text <| "State") + , el [ alignRight ] <| text <| stats.state + ] + ] + ] + + Running stats -> + column + [ Border.color (Element.rgb255 198 205 214) + , Border.width 2 + , padding 10 + , spacing 5 + , alignTop + , width fill + ] + (viewNodeStats stats model.showNodeStats model.zone) Nothing -> Element.none @@ -1924,7 +1969,7 @@ viewPanel viewContent = viewContent -viewNodeStats : NodeStats -> Bool -> Time.Zone -> List (Element Msg) +viewNodeStats : RunningNodeStats -> Bool -> Time.Zone -> List (Element Msg) viewNodeStats nodeStats showNodeStats timeZone = [ row [ width fill, Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, paddingXY 2 10 ] [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Node Status" -- GitLab From 39b82cfe78fb9c5d0faa99ca314c2c72d5fe5131 Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 7 Feb 2020 22:06:33 +0000 Subject: [PATCH 3/3] Resolve "Implement RemoteData Package" --- CHANGELOG.md | 16 +- elm.json | 1 + index.html | 5 + src/OxHead.elm | 1192 +++++++++++++++++++----------------------------- 4 files changed, 500 insertions(+), 714 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d8c277..79333fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [0.25.0] - 2020-02-07 + +### Added + +- [RemoteData Package](https://package.elm-lang.org/packages/krisajenkins/remotedata/latest/) +- Message when Data is yet to be Fetched +- Spinner when Loading Data + +## Changed + +- Fetched Data is now represented with RemoteData Datatype + ## [0.24.0] - 2020-01-28 ### Added @@ -113,11 +125,11 @@ ### Added -- [DateFormat Library](https://package.elm-lang.org/packages/ryannhg/date-format/) +- [DateFormat Package](https://package.elm-lang.org/packages/ryannhg/date-format/latest/) - Current Time to compare with schedules - Countdown to next block - Stake Pools Leaders sorted by `created_at` -- [Elm ISO8601 Date Strings Library](https://package.elm-lang.org/packages/rtfeldman/elm-iso8601-date-strings/latest/) +- [Elm ISO8601 Date Strings Package](https://package.elm-lang.org/packages/rtfeldman/elm-iso8601-date-strings/latest/) ### Changed diff --git a/elm.json b/elm.json index 326b673..23ae342 100644 --- a/elm.json +++ b/elm.json @@ -16,6 +16,7 @@ "elm/svg": "1.0.1", "elm/time": "1.0.0", "elm/url": "1.0.0", + "krisajenkins/remotedata": "6.0.1", "mdgriffith/elm-style-animation": "4.0.0", "mdgriffith/elm-ui": "1.1.5", "rtfeldman/elm-iso8601-date-strings": "1.1.3", diff --git a/index.html b/index.html index bbd3e15..a9aea68 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,11 @@ + diff --git a/src/OxHead.elm b/src/OxHead.elm index 9eb8869..8aa2e24 100644 --- a/src/OxHead.elm +++ b/src/OxHead.elm @@ -4,7 +4,7 @@ import Browser import Browser.Events import Browser.Navigation as Nav import DateFormat.Relative as RelativeDateFormat -import Element exposing (Device, DeviceClass(..), Element, Orientation(..), alignBottom, alignLeft, alignRight, alignTop, centerX, centerY, classifyDevice, clipX, column, el, fill, fillPortion, height, image, link, maximum, minimum, newTabLink, onLeft, onRight, padding, paddingEach, paddingXY, px, rgb255, row, scrollbarY, spacing, text, width, wrappedRow) +import Element exposing (Device, DeviceClass(..), Element, Orientation(..), alignBottom, alignLeft, alignRight, alignTop, centerX, centerY, classifyDevice, column, el, fill, fillPortion, height, image, link, maximum, minimum, newTabLink, onLeft, onRight, padding, paddingEach, paddingXY, px, rgb255, row, scrollbarY, spacing, text, width, wrappedRow) import Element.Background as Background import Element.Border as Border import Element.Events as Events @@ -17,6 +17,7 @@ import Http exposing (get) import Iso8601 import Json.Decode exposing (index, int, list, map2, string) import Json.Decode.Pipeline exposing (required) +import RemoteData exposing (RemoteData(..), WebData) import Svg exposing (circle, defs, g, polygon, polyline, rect, svg) import Svg.Attributes as SvgA import Task @@ -69,34 +70,34 @@ type alias Model = , status : Status , nodeUrl : String , isMonitoring : Bool - , nodeStats : Maybe NodeStats + , nodeStats : WebData NodeStats , showNodeStats : Bool - , networkStatsList : Maybe (List NetworkStats) + , networkStatsList : WebData (List NetworkStats) , showNetworkStats : Bool - , nodeSettings : Maybe NodeSettings + , nodeSettings : WebData NodeSettings , showNodeSettings : Bool - , utxoList : Maybe (List UTXO) + , utxoList : WebData (List UTXO) , showUTXOList : Bool - , tip : Maybe String + , tip : WebData String , showTip : Bool - , stakePoolsList : Maybe (List String) + , stakePoolsList : WebData (List String) , showStakePoolsList : Bool - , stakeDistribution : Maybe StakeDistribution + , stakeDistribution : WebData StakeDistribution , showStakeDistribution : Bool - , stakePoolDetail : Maybe StakePool + , stakePoolDetail : WebData StakePool , stakePoolID : String , myStakePoolID : String , showZeroStaked : Bool , sortAscending : Bool - , leaders : Maybe (List Int) + , leaders : WebData (List Int) , showLeaders : Bool - , leadersLogs : Maybe (List LeadersLogs) + , leadersLogs : WebData (List LeadersLogs) , showLeadersLogsList : Bool - , fragmentLogs : Maybe (List FragmentLogs) + , fragmentLogs : WebData (List FragmentLogs) , showFragmentLogsList : Bool - , block : Maybe String - , nextBlock : Maybe String - , account : Maybe Account + , block : WebData String + , nextBlock : WebData String + , account : WebData Account , blockId : String , accountId : String , nextBlockCount : Int @@ -118,31 +119,31 @@ initialModel url key = , status = Loaded , nodeUrl = "http://127.0.0.1:3100" , isMonitoring = False - , nodeStats = Nothing + , nodeStats = NotAsked , showNodeStats = True , showNetworkStats = False - , networkStatsList = Nothing - , nodeSettings = Nothing + , networkStatsList = NotAsked + , nodeSettings = NotAsked , showNodeSettings = True - , utxoList = Nothing - , tip = Nothing + , utxoList = NotAsked + , tip = NotAsked , showUTXOList = True - , stakePoolsList = Nothing - , stakeDistribution = Nothing - , stakePoolDetail = Nothing + , stakePoolsList = NotAsked + , stakeDistribution = NotAsked + , stakePoolDetail = NotAsked , stakePoolID = "" , myStakePoolID = "" , showTip = True - , leaders = Nothing - , leadersLogs = Nothing + , leaders = NotAsked + , leadersLogs = NotAsked , showStakePoolsList = True - , fragmentLogs = Nothing - , block = Nothing + , fragmentLogs = NotAsked + , block = NotAsked , showStakeDistribution = True , showZeroStaked = False , sortAscending = False - , nextBlock = Nothing - , account = Nothing + , nextBlock = NotAsked + , account = NotAsked , showLeaders = True , blockId = "" , accountId = "" @@ -207,22 +208,22 @@ type Msg = LinkClicked Browser.UrlRequest | UrlChanged Url.Url | FetchDataClicked - | GotNodeStatsMessage (Result Http.Error NodeStats) - | GotNetworkStatsListMessage (Result Http.Error (List NetworkStats)) - | GotNodeSettingsMessage (Result Http.Error NodeSettings) - | GotUTXOListMessage (Result Http.Error (List UTXO)) - | GotTipMessage (Result Http.Error String) - | GotStakePoolsListMessage (Result Http.Error (List String)) - | GotStakePoolMessage (Result Http.Error StakePool) - | GotStakeDistributionMessage (Result Http.Error StakeDistribution) - | GotLeadersMessage (Result Http.Error (List Int)) - | GotLeadersLogsListMessage (Result Http.Error (List LeadersLogs)) - | GotFragmentLogsListMessage (Result Http.Error (List FragmentLogs)) - | GotBlockMessage (Result Http.Error String) - | GotNextBlockMessage (Result Http.Error String) - | GotAccountMessage (Result Http.Error Account) - | GotShutdownMessage (Result Http.Error String) - | GotDeleteLeaderMessage (Result Http.Error String) + | GotNodeStatsMessage (WebData NodeStats) + | GotNetworkStatsListMessage (WebData (List NetworkStats)) + | GotNodeSettingsMessage (WebData NodeSettings) + | GotUTXOListMessage (WebData (List UTXO)) + | GotTipMessage (WebData String) + | GotStakePoolsListMessage (WebData (List String)) + | GotStakePoolMessage (WebData StakePool) + | GotStakeDistributionMessage (WebData StakeDistribution) + | GotLeadersMessage (WebData (List Int)) + | GotLeadersLogsListMessage (WebData (List LeadersLogs)) + | GotFragmentLogsListMessage (WebData (List FragmentLogs)) + | GotBlockMessage (WebData String) + | GotNextBlockMessage (WebData String) + | GotAccountMessage (WebData Account) + | GotShutdownMessage (WebData String) + | GotDeleteLeaderMessage (WebData String) | ToggleMonitoring | ToggleNodeStatsPanel | ToggleNodeSettingsPanel @@ -262,7 +263,7 @@ type Msg type Status - = Loading + = Fetching | Loaded | Errored String @@ -294,7 +295,7 @@ getNetworkStatsList : Model -> Cmd Msg getNetworkStatsList model = Http.get { url = model.nodeUrl ++ "/api/v0/network/stats" - , expect = expectJson GotNetworkStatsListMessage (list decodeNetworkStats) + , expect = expectJson (RemoteData.fromResult >> GotNetworkStatsListMessage) (list decodeNetworkStats) } @@ -359,7 +360,7 @@ getNodeStats : Model -> Cmd Msg getNodeStats model = Http.get { url = model.nodeUrl ++ "/api/v0/node/stats" - , expect = expectJson GotNodeStatsMessage (Json.Decode.oneOf [ Json.Decode.map Initializing decodeBootstrappingNodeStats, Json.Decode.map Running decodeRunningNodeStats ]) + , expect = expectJson (RemoteData.fromResult >> GotNodeStatsMessage) (Json.Decode.oneOf [ Json.Decode.map Running decodeRunningNodeStats, Json.Decode.map Initializing decodeBootstrappingNodeStats ]) } @@ -456,7 +457,7 @@ getNodeSettings : Model -> Cmd Msg getNodeSettings model = Http.get { url = model.nodeUrl ++ "/api/v0/settings" - , expect = expectJson GotNodeSettingsMessage decodeNodeSettings + , expect = expectJson (RemoteData.fromResult >> GotNodeSettingsMessage) decodeNodeSettings } @@ -485,7 +486,7 @@ getUTXOList : Model -> Cmd Msg getUTXOList model = Http.get { url = model.nodeUrl ++ "/api/v0/utxo" - , expect = expectJson GotUTXOListMessage (list decodeUTXO) + , expect = expectJson (RemoteData.fromResult >> GotUTXOListMessage) (list decodeUTXO) } @@ -497,7 +498,7 @@ getTip : Model -> Cmd Msg getTip model = Http.get { url = model.nodeUrl ++ "/api/v0/tip" - , expect = Http.expectString GotTipMessage + , expect = Http.expectString (RemoteData.fromResult >> GotTipMessage) } @@ -568,7 +569,7 @@ getStakePool : Model -> String -> Cmd Msg getStakePool model stakePoolID = Http.get { url = model.nodeUrl ++ "/api/v0/stake_pool/" ++ stakePoolID - , expect = Http.expectJson GotStakePoolMessage decodeStakePool + , expect = Http.expectJson (RemoteData.fromResult >> GotStakePoolMessage) decodeStakePool } @@ -614,7 +615,7 @@ getStakeDistribution : Model -> Cmd Msg getStakeDistribution model = Http.get { url = model.nodeUrl ++ "/api/v0/stake" - , expect = expectJson GotStakeDistributionMessage decodeStakeDistribution + , expect = expectJson (RemoteData.fromResult >> GotStakeDistributionMessage) decodeStakeDistribution } @@ -626,7 +627,7 @@ getLeaders : Model -> Cmd Msg getLeaders model = Http.get { url = model.nodeUrl ++ "/api/v0/leaders" - , expect = Http.expectJson GotLeadersMessage (Json.Decode.list Json.Decode.int) + , expect = Http.expectJson (RemoteData.fromResult >> GotLeadersMessage) (Json.Decode.list Json.Decode.int) } @@ -713,7 +714,7 @@ getLeadersLogsList : Model -> Cmd Msg getLeadersLogsList model = Http.get { url = model.nodeUrl ++ "/api/v0/leaders/logs" - , expect = expectJson GotLeadersLogsListMessage (list decodeLeadersLogs) + , expect = expectJson (RemoteData.fromResult >> GotLeadersLogsListMessage) (list decodeLeadersLogs) } @@ -744,7 +745,7 @@ getFragmentLogsList : Model -> Cmd Msg getFragmentLogsList model = Http.get { url = model.nodeUrl ++ "/api/v0/fragment/logs" - , expect = expectJson GotFragmentLogsListMessage (list decodeFragmentLogs) + , expect = expectJson (RemoteData.fromResult >> GotFragmentLogsListMessage) (list decodeFragmentLogs) } @@ -756,7 +757,7 @@ getBlock : Model -> Cmd Msg getBlock model = Http.get { url = model.nodeUrl ++ "/api/v0/block/" ++ model.blockId - , expect = Http.expectString GotBlockMessage + , expect = Http.expectString (RemoteData.fromResult >> GotBlockMessage) } @@ -778,7 +779,7 @@ getNextBlock model = else "" ) - , expect = Http.expectString GotNextBlockMessage + , expect = Http.expectString (RemoteData.fromResult >> GotNextBlockMessage) } @@ -816,7 +817,7 @@ getAccount : Model -> Cmd Msg getAccount model = Http.get { url = model.nodeUrl ++ "/api/v0/account/" ++ model.accountId - , expect = Http.expectJson GotAccountMessage decodeAccount + , expect = Http.expectJson (RemoteData.fromResult >> GotAccountMessage) decodeAccount } @@ -831,7 +832,7 @@ deleteLeader model = , headers = [] , url = model.nodeUrl ++ "/api/v0/leaders/" ++ model.leaderId , body = Http.emptyBody - , expect = Http.expectString GotDeleteLeaderMessage + , expect = Http.expectString (RemoteData.fromResult >> GotDeleteLeaderMessage) , timeout = Nothing , tracker = Nothing } @@ -845,7 +846,7 @@ getShutdown : Model -> Cmd Msg getShutdown model = Http.get { url = model.nodeUrl ++ "/api/v0/shutdown" - , expect = Http.expectString GotShutdownMessage + , expect = Http.expectString (RemoteData.fromResult >> GotShutdownMessage) } @@ -870,11 +871,11 @@ update msg model = FetchDataClicked -> case toRoute (Url.toString model.url) of Node -> - ( { model | status = Loading }, Cmd.batch <| List.map (\x -> x model) [ getNodeStats, getNetworkStatsList, getNodeSettings ] ) + ( { model | status = Fetching, nodeStats = Loading, networkStatsList = Loading, nodeSettings = Loading }, Cmd.batch <| List.map (\x -> x model) [ getNodeStats, getNetworkStatsList, getNodeSettings ] ) Distribution -> - ( { model | status = Loading } - , if model.stakePoolDetail == Nothing && not (String.isEmpty model.stakePoolID) then + ( { model | status = Fetching, stakeDistribution = Loading } + , if model.stakePoolDetail == NotAsked && not (String.isEmpty model.stakePoolID) then Cmd.batch [ getStakeDistribution model, getStakePool model model.stakePoolID ] else @@ -882,7 +883,7 @@ update msg model = ) Leaders -> - ( { model | status = Loading }, getLeadersLogsList model ) + ( { model | status = Fetching, leadersLogs = Loading }, getLeadersLogsList model ) _ -> ( model, Cmd.none ) @@ -918,11 +919,11 @@ update msg model = Monitor _ -> case toRoute (Url.toString model.url) of Node -> - ( { model | status = Loading }, Cmd.batch <| List.map (\x -> x model) [ getNodeStats, getNetworkStatsList, getNodeSettings ] ) + ( { model | status = Fetching }, Cmd.batch <| List.map (\x -> x model) [ getNodeStats, getNetworkStatsList, getNodeSettings ] ) Distribution -> - ( { model | status = Loading } - , if model.stakePoolDetail == Nothing && not (String.isEmpty model.stakePoolID) then + ( { model | status = Fetching } + , if model.stakePoolDetail == NotAsked && not (String.isEmpty model.stakePoolID) then Cmd.batch [ getStakeDistribution model, getStakePool model model.stakePoolID ] else @@ -930,7 +931,7 @@ update msg model = ) Leaders -> - ( { model | status = Loading }, getLeadersLogsList model ) + ( { model | status = Fetching }, getLeadersLogsList model ) _ -> ( model, Cmd.none ) @@ -971,326 +972,54 @@ update msg model = ToggleFragmentLogsPanel -> ( { model | showFragmentLogsList = not model.showFragmentLogsList }, Cmd.none ) - GotNodeStatsMessage (Ok response) -> - ( { model | status = Loaded, nodeStats = Just response }, Cmd.none ) + GotNodeStatsMessage response -> + ( { model | status = Loaded, nodeStats = response }, Cmd.none ) - GotNodeStatsMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Node Status: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Node Status: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Node Status: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Node Status: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Node Status: " ++ err) }, Cmd.none ) - - GotNetworkStatsListMessage (Ok response) -> - ( { model | status = Loaded, networkStatsList = Just response }, Cmd.none ) - - GotNetworkStatsListMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Network Status: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Network Status: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Network Status: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Network Status: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Network Status: " ++ err) }, Cmd.none ) - - GotNodeSettingsMessage (Ok response) -> - ( { model | status = Loaded, nodeSettings = Just response }, Cmd.none ) - - GotNodeSettingsMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Node Settings: " ++ url) }, Cmd.none ) + GotNetworkStatsListMessage response -> + ( { model | status = Loaded, networkStatsList = response }, Cmd.none ) - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Node Settings: " ++ "Timeout") }, Cmd.none ) + GotNodeSettingsMessage response -> + ( { model | status = Loaded, nodeSettings = response }, Cmd.none ) - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Node Settings: " ++ "Network") }, Cmd.none ) + GotUTXOListMessage response -> + ( { model | status = Loaded, utxoList = response }, Cmd.none ) - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Node Settings: " ++ String.fromInt statusCode) }, Cmd.none ) + GotTipMessage response -> + ( { model | status = Loaded, tip = response }, Cmd.none ) - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Node Settings: " ++ err), error = err }, Cmd.none ) + GotStakePoolsListMessage response -> + ( { model | status = Loaded, stakePoolsList = response }, Cmd.none ) - GotUTXOListMessage (Ok response) -> - ( { model | status = Loaded, utxoList = Just response }, Cmd.none ) + GotStakeDistributionMessage response -> + ( { model | status = Loaded, stakeDistribution = response }, Cmd.none ) - GotUTXOListMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve UTXO List: " ++ url) }, Cmd.none ) + GotStakePoolMessage response -> + ( { model | status = Loaded, stakePoolDetail = response }, Cmd.none ) - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve UTXO List: " ++ "Timeout") }, Cmd.none ) + GotLeadersMessage response -> + ( { model | status = Loaded, leaders = response }, Cmd.none ) - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve UTXO List: " ++ "Network") }, Cmd.none ) + GotLeadersLogsListMessage response -> + ( { model | status = Loaded, leadersLogs = response }, Cmd.none ) - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve UTXO List: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve UTXO List: " ++ err) }, Cmd.none ) + GotFragmentLogsListMessage response -> + ( { model | status = Loaded, fragmentLogs = response }, Cmd.none ) - GotTipMessage (Ok response) -> - ( { model | status = Loaded, tip = Just response }, Cmd.none ) + GotBlockMessage response -> + ( { model | status = Loaded, block = response }, Cmd.none ) - GotTipMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ err) }, Cmd.none ) - - GotStakePoolsListMessage (Ok response) -> - ( { model | status = Loaded, stakePoolsList = Just response }, Cmd.none ) - - GotStakePoolsListMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ "Timeout") }, Cmd.none ) + GotNextBlockMessage response -> + ( { model | status = Loaded, nextBlock = response }, Cmd.none ) - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Stake Pools: " ++ err) }, Cmd.none ) - - GotStakeDistributionMessage (Ok response) -> - ( { model | status = Loaded, stakeDistribution = Just response }, Cmd.none ) - - GotStakeDistributionMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Stake Distribution: " ++ url), error = url }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Stake Distribution: " ++ "Timeout"), error = "Timeout" }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Stake Distribution: " ++ "Network"), error = "Network" }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Stake Distribution: " ++ String.fromInt statusCode), error = String.fromInt statusCode }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Stake Distribution: " ++ err), error = err }, Cmd.none ) - - GotStakePoolMessage (Ok response) -> - ( { model | status = Loaded, stakePoolDetail = Just response }, Cmd.none ) - - GotStakePoolMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Stake Pool: " ++ url), error = url }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Stake Pool: " ++ "Timeout"), error = "Timeout" }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Stake Pool: " ++ "Network"), error = "Network" }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Stake Pool: " ++ String.fromInt statusCode), error = String.fromInt statusCode }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Stake Pool: " ++ err), error = err }, Cmd.none ) - - GotLeadersMessage (Ok response) -> - ( { model | status = Loaded, leaders = Just response }, Cmd.none ) - - GotLeadersMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Leaders: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Leaders: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Leaders: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Leaders: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Leaders: " ++ err) }, Cmd.none ) - - GotLeadersLogsListMessage (Ok response) -> - ( { model | status = Loaded, leadersLogs = Just response }, Cmd.none ) - - GotLeadersLogsListMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Leaders Logs List: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Leaders Logs List: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Leaders Logs List: " ++ "Network") }, Cmd.none ) + GotAccountMessage response -> + ( { model | status = Loaded, account = response }, Cmd.none ) - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Leaders Logs List: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Leaders Logs List: " ++ err) }, Cmd.none ) - - GotFragmentLogsListMessage (Ok response) -> - ( { model | status = Loaded, fragmentLogs = Just response }, Cmd.none ) - - GotFragmentLogsListMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Fragment Logs List: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Fragment Logs List: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Fragment Logs List: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Fragment Logs List: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Fragment Logs List: " ++ err) }, Cmd.none ) - - GotBlockMessage (Ok response) -> - ( { model | status = Loaded, block = Just response }, Cmd.none ) - - GotBlockMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Block: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Block: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Block: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Block: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Block: " ++ err) }, Cmd.none ) - - GotNextBlockMessage (Ok response) -> - ( { model | status = Loaded, nextBlock = Just response }, Cmd.none ) - - GotNextBlockMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Next Block: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Next Block: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Next Block: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Next Block: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Next Block: " ++ err) }, Cmd.none ) - - GotAccountMessage (Ok response) -> - ( { model | status = Loaded, account = Just response }, Cmd.none ) - - GotAccountMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't retrieve Account: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't retrieve Account: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't retrieve Account: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't retrieve Account: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't retrieve Account: " ++ err) }, Cmd.none ) - - GotDeleteLeaderMessage (Ok _) -> + GotDeleteLeaderMessage _ -> ( { model | status = Loaded }, Cmd.none ) - GotDeleteLeaderMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't Delete Leader: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't Delete Leader: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't Delete Leader: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't Delete Leader: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't Delete Leader: " ++ err) }, Cmd.none ) - - GotShutdownMessage (Ok _) -> + GotShutdownMessage _ -> ( { model | status = Loaded }, Cmd.none ) - GotShutdownMessage (Err error) -> - case error of - Http.BadUrl url -> - ( { model | status = Errored ("Couldn't Shutdown Node: " ++ url) }, Cmd.none ) - - Http.Timeout -> - ( { model | status = Errored ("Couldn't Shutdown Node: " ++ "Timeout") }, Cmd.none ) - - Http.NetworkError -> - ( { model | status = Errored ("Couldn't Shutdown Node: " ++ "Network") }, Cmd.none ) - - Http.BadStatus statusCode -> - ( { model | status = Errored ("Couldn't Shutdown Node: " ++ String.fromInt statusCode) }, Cmd.none ) - - Http.BadBody err -> - ( { model | status = Errored ("Couldn't Shutdown Node: " ++ err) }, Cmd.none ) - StakePoolIdClicked id -> ( { model | stakePoolID = id }, getStakePool model id ) @@ -1299,7 +1028,7 @@ update msg model = ( { model | stakePoolID = id }, getStakePool model id ) else - ( { model | stakePoolID = id, stakePoolDetail = Nothing }, Cmd.none ) + ( { model | stakePoolID = id, stakePoolDetail = NotAsked }, Cmd.none ) NodeURLChanged url -> let @@ -1442,7 +1171,7 @@ view model = row [ height fill, width fill ] [ viewSidebar model , column [ width fill, height fill, scrollbarY ] - [ column [ width fill, height fill, paddingXY 10 0, spacing 10 ] + [ column [ width fill, height fill, spacing 10 ] [ -- , row [] -- [ Input.text [ centerY, width (px 300) ] { onChange = NodeURLChanged, text = model.nodeUrl, placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Insert Node URL")), label = Input.labelLeft [ centerY ] (text "Node URL: ") } -- ] @@ -1491,18 +1220,17 @@ view model = viewHome Node -> - viewNode model + if RemoteData.isSuccess model.nodeStats && RemoteData.isFailure model.nodeSettings && RemoteData.isFailure model.networkStatsList then + webDataView (viewNodeStats model.showNodeStats model.zone) model.nodeStats + + else + webDataView (viewNode model) (merge3 model.nodeStats model.nodeSettings model.networkStatsList) Distribution -> viewDistribution model Leaders -> - case model.leadersLogs of - Just leadersLogsList -> - viewLeadersLogs model.time leadersLogsList model.zone - - Nothing -> - Element.none + webDataView (viewLeadersLogs model.time model.zone) model.leadersLogs Settings -> viewSettings model @@ -1534,15 +1262,19 @@ viewSidebar model = ] , column [ centerX, spacing 40 ] [ el [ centerX, Font.color (Element.rgb255 94 96 102), Element.mouseOver [ Font.color (Element.rgb 225 225 225) ] ] (Input.button [] { onPress = Just FetchDataClicked, label = withTooltip "Fetch Data" <| fetchDataSvg }) - , Input.button [ centerX, Font.color (Element.rgb255 94 96 102), Element.mouseOver [ Font.color (Element.rgb 225 225 225) ] ] - { onPress = Just ToggleMonitoring - , label = - if model.isMonitoring then - withTooltip "Stop Monitoring" <| monitorSvg + , if model.isMonitoring then + Input.button [ centerX, Font.color (Element.rgb 225 225 225), Element.mouseOver [ Font.color (Element.rgb 225 225 225) ] ] + { onPress = Just ToggleMonitoring + , label = + withTooltip "Stop Monitoring" <| el [ Element.htmlAttribute <| Html.Attributes.style "-webkit-animation" "1s spinner infinite" ] <| monitorSvg + } - else + else + Input.button [ centerX, Font.color (Element.rgb255 94 96 102), Element.mouseOver [ Font.color (Element.rgb 225 225 225) ] ] + { onPress = Just ToggleMonitoring + , label = withTooltip "Start Monitoring" <| monitorSvg - } + } , el [ alignBottom , centerX @@ -1605,20 +1337,49 @@ viewSidebar model = , paddingXY 10 0 , centerX ] - [ el [ centerX ] <| - case model.status of - Loading -> - loadingSvg + [ case model.status of + Fetching -> + el [ centerX, Element.htmlAttribute <| Html.Attributes.style "-webkit-animation" "1s spinner infinite" ] <| fetchingSvg - Loaded -> - loadedSvg + Loaded -> + el [ centerX ] <| loadedSvg - Errored _ -> - erroredSvg + Errored _ -> + el [ centerX ] <| erroredSvg ] ] +webDataView : (a -> Element Msg) -> WebData a -> Element Msg +webDataView successView webData = + case webData of + NotAsked -> + el [ centerX, centerY, fontMono ] <| text "Nothing to see here, fetch some data!" + + Loading -> + el [ centerX, centerY, Element.htmlAttribute <| Html.Attributes.style "-webkit-animation" "1s spinner infinite" ] <| loadingSvg + + Failure err -> + case err of + Http.BadUrl url -> + text <| "Couldn't retrieve: " ++ url + + Http.Timeout -> + text <| "Couldn't retrieve: " ++ "Timeout" + + Http.NetworkError -> + text <| "Couldn't retrieve: " ++ "Network" + + Http.BadStatus statusCode -> + text <| "Couldn't retrieve: " ++ String.fromInt statusCode + + Http.BadBody error -> + text <| "Couldn't retrieve: " ++ error + + Success data -> + successView data + + viewHome : Element Msg viewHome = column [ padding 20, spacing 20 ] @@ -1670,8 +1431,19 @@ viewHome = ] -viewNode : Model -> Element Msg -viewNode model = +merge3 : + RemoteData e a + -> RemoteData e b + -> RemoteData e c + -> RemoteData e ( a, b, c ) +merge3 a b c = + RemoteData.map (\x y z -> ( x, y, z )) a + |> RemoteData.andMap b + |> RemoteData.andMap c + + +viewNode : Model -> ( NodeStats, NodeSettings, List NetworkStats ) -> Element Msg +viewNode model ( nodeStats, nodeSettings, networkStats ) = column [ Font.size 14 , fontMono @@ -1681,165 +1453,126 @@ viewNode model = ] [ wrappedRow [ width fill, spacing 10 ] - [ case model.nodeStats of - Just nodeStats -> - case nodeStats of - Initializing stats -> - column - [ Border.color (Element.rgb255 198 205 214) - , Border.width 2 - , padding 10 - , spacing 5 - , alignTop - , width fill - ] - [ row [ width fill, Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, paddingXY 2 10 ] - [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Node Status" - ] - , column - [ width fill, spacing 10, padding 10 ] - [ wrappedRow [ width fill ] - [ el [ alignLeft ] (withTooltip "Node app version" <| text <| "Version") - , el [ alignRight ] <| text <| stats.version - ] - , wrappedRow [ width fill ] - [ el [ alignLeft ] (withTooltip "State of the node" <| text <| "State") - , el [ alignRight ] <| text <| stats.state - ] - ] - ] + [ viewNodeStats model.showNodeStats model.zone nodeStats + , viewNodeSettings model.showNodeSettings model.zone nodeSettings + ] + , viewNetworkStats model.showNetworkStats model.zone networkStats + ] - Running stats -> - column - [ Border.color (Element.rgb255 198 205 214) - , Border.width 2 - , padding 10 - , spacing 5 - , alignTop - , width fill - ] - (viewNodeStats stats model.showNodeStats model.zone) - - Nothing -> - Element.none - , case model.nodeSettings of - Just stats -> - column - [ Border.color (Element.rgb255 198 205 214) - , Border.width 2 - , padding 10 - , spacing 5 - , alignTop - , width fill + + +-- , case model.utxoList of +-- Just stats -> +-- viewPanel (viewUTXOList stats) +-- Nothing -> +-- Element.none +-- , case model.tip of +-- Just tip -> +-- el [] <| text tip +-- Nothing -> +-- Element.none +-- , case model.stakePoolsList of +-- Just stakePoolsList -> +-- viewPanel (viewStakePools stakePoolsList) +-- Nothing -> +-- Element.none +-- , case model.stakeDistribution of +-- Just stakeDistribution -> +-- viewPanel (viewStakeDistribution stakeDistribution) +-- Nothing -> +-- Element.none +-- , case model.leaders of +-- Just leadersList -> +-- viewPanel (viewLeaders leadersList) +-- Nothing -> +-- Element.none +-- , case model.leadersLogs of +-- Just leadersLogsList -> +-- viewPanel (viewLeadersLogs leadersLogsList) +-- Nothing -> +-- Element.none +-- , case model.fragmentLogs of +-- Just fragmentLogsList -> +-- viewPanel (viewFragmentLogs fragmentLogsList) +-- Nothing -> +-- Element.none +-- , case model.account of +-- Just acc -> +-- viewPanel (viewAccount acc) +-- Nothing -> +-- Element.none + + +viewNodeStats : Bool -> Time.Zone -> NodeStats -> Element Msg +viewNodeStats showNodeStats zone nodeStats = + case nodeStats of + Initializing stats -> + column + [ padding 10 + , spacing 5 + , alignTop + , width fill + , fontMono + , Background.color (Element.rgb255 255 214 68) + ] + [ row [ width fill, Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, paddingXY 2 10 ] + [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Node Status" + ] + , column + [ width fill, spacing 10, padding 10 ] + [ wrappedRow [ width fill ] + [ el [ alignLeft ] (withTooltip "Node app version" <| text <| "Version") + , el [ alignRight ] <| text <| stats.version + ] + , wrappedRow [ width fill ] + [ el [ alignLeft ] (withTooltip "State of the node" <| text <| "State") + , el [ alignRight ] <| text <| stats.state ] - (viewNodeSettings stats model.showNodeSettings model.zone) - - Nothing -> - Element.none - - -- , case model.utxoList of - -- Just stats -> - -- viewPanel (viewUTXOList stats) - -- Nothing -> - -- Element.none - -- , case model.tip of - -- Just tip -> - -- el [] <| text tip - -- Nothing -> - -- Element.none - -- , case model.stakePoolsList of - -- Just stakePoolsList -> - -- viewPanel (viewStakePools stakePoolsList) - -- Nothing -> - -- Element.none - -- , case model.stakeDistribution of - -- Just stakeDistribution -> - -- viewPanel (viewStakeDistribution stakeDistribution) - -- Nothing -> - -- Element.none - -- , case model.leaders of - -- Just leadersList -> - -- viewPanel (viewLeaders leadersList) - -- Nothing -> - -- Element.none - -- , case model.leadersLogs of - -- Just leadersLogsList -> - -- viewPanel (viewLeadersLogs leadersLogsList) - -- Nothing -> - -- Element.none - -- , case model.fragmentLogs of - -- Just fragmentLogsList -> - -- viewPanel (viewFragmentLogs fragmentLogsList) - -- Nothing -> - -- Element.none - -- , case model.account of - -- Just acc -> - -- viewPanel (viewAccount acc) - -- Nothing -> - -- Element.none - ] - , case model.networkStatsList of - Just networkStatsData -> - column - [ Border.color (Element.rgb255 198 205 214) - , Border.width 2 - , padding 10 - , spacing 5 - , alignTop - , width fill ] - (viewNetworkStats networkStatsData model.showNetworkStats model.zone) + ] - Nothing -> - Element.none - ] + Running stats -> + column + [ Border.color (Element.rgb255 198 205 214) + , Border.width 2 + , padding 10 + , spacing 5 + , alignTop + , width fill + ] + (viewRunningNodeStats stats showNodeStats zone) viewDistribution : Model -> Element Msg viewDistribution model = - row - [ Font.size 14 - , fontMono - , spacing 15 - , padding 10 - , width fill - ] - -- [ case model.stakePoolsList of - -- Just stakePoolsList -> - -- viewPanel (viewStakePools stakePoolsList model.showStakePoolsList) - -- Nothing -> - -- Element.none - [ case model.stakeDistribution of - Just stakeDistribution -> - column [ centerX, width fill ] - (viewStakeDistribution stakeDistribution - model.stakePoolDetail - model.stakePoolID - model.myStakePoolID - model.showZeroStaked - model.sortAscending - model.showStakePoolInputSuggestions - ) + webDataView + (viewStakeDistribution + model.stakePoolDetail + model.stakePoolID + model.myStakePoolID + model.showZeroStaked + model.sortAscending + model.showStakePoolInputSuggestions + ) + model.stakeDistribution - Nothing -> - Element.none - -- , case model.leaders of - -- Just leadersList -> - -- viewPanel (viewLeaders leadersList) - -- Nothing -> - -- Element.none - -- , case model.leadersLogs of - -- Just leadersLogsList -> - -- viewPanel (viewLeadersLogs leadersLogsList) - -- Nothing -> - -- Element.none - -- , case model.fragmentLogs of - -- Just fragmentLogsList -> - -- viewPanel (viewFragmentLogs fragmentLogsList) - -- Nothing -> - -- Element.none - ] + +-- , case model.leaders of +-- Just leadersList -> +-- viewPanel (viewLeaders leadersList) +-- Nothing -> +-- Element.none +-- , case model.leadersLogs of +-- Just leadersLogsList -> +-- viewPanel (viewLeadersLogs leadersLogsList) +-- Nothing -> +-- Element.none +-- , case model.fragmentLogs of +-- Just fragmentLogsList -> +-- viewPanel (viewFragmentLogs fragmentLogsList) +-- Nothing -> +-- Element.none viewSettings : Model -> Element Msg @@ -1969,8 +1702,8 @@ viewPanel viewContent = viewContent -viewNodeStats : RunningNodeStats -> Bool -> Time.Zone -> List (Element Msg) -viewNodeStats nodeStats showNodeStats timeZone = +viewRunningNodeStats : RunningNodeStats -> Bool -> Time.Zone -> List (Element Msg) +viewRunningNodeStats nodeStats showNodeStats timeZone = [ row [ width fill, Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, paddingXY 2 10 ] [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Node Status" , Input.button [ alignRight ] { onPress = Just ToggleNodeStatsPanel, label = toggleIcon showNodeStats } @@ -2036,116 +1769,132 @@ viewNodeStats nodeStats showNodeStats timeZone = ] -viewNetworkStats : List NetworkStats -> Bool -> Time.Zone -> List (Element Msg) -viewNetworkStats networkStats showNetworkStats timeZone = - [ column [ width fill, spacing 10 ] - [ row [ Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, width fill, paddingXY 2 10 ] - [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Network Status" - , Input.button toggleButtonStyle { onPress = Just ToggleNetworkStatsPanel, label = toggleIcon showNetworkStats } - ] - , row [ fontNormal, Font.size 16, width fill, Font.bold ] - [ el [ alignLeft ] <| text <| "Connections" - , el [ alignRight ] <| text <| String.fromInt (List.length networkStats) - ] +viewNetworkStats : Bool -> Time.Zone -> List NetworkStats -> Element Msg +viewNetworkStats showNetworkStats timeZone networkStats = + column + [ Border.color (Element.rgb255 198 205 214) + , Border.width 2 + , padding 10 + , spacing 5 + , alignTop + , width fill ] - , if showNetworkStats then - column - [ width fill - , spacing 10 - , padding 10 - , scrollbarY - , height (fill |> maximum 400) + [ column [ width fill, spacing 10 ] + [ row [ Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, width fill, paddingXY 2 10 ] + [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Network Status" + , Input.button toggleButtonStyle { onPress = Just ToggleNetworkStatsPanel, label = toggleIcon showNetworkStats } + ] + , row [ fontNormal, Font.size 16, width fill, Font.bold ] + [ el [ alignLeft ] <| text <| "Connections" + , el [ alignRight ] <| text <| String.fromInt (List.length networkStats) + ] ] - (List.map - (\stats -> - column [ width fill, padding 20, Background.color (Element.rgb255 240 241 242), Border.solid, Border.color (Element.rgb255 198 205 214), Border.widthEach { bottom = 0, left = 2, right = 0, top = 0 } ] - [ wrappedRow [ width fill ] - [ withTooltip "Hex-encoded node ID" <| el [ alignLeft ] <| text <| "Node ID" - , el [ alignRight ] <| text <| stats.nodeId - ] - , wrappedRow [ width fill ] - [ withTooltip "Timestamp from when the connection was established at" <| el [ alignLeft ] <| text <| "Established At" - , el [ alignRight ] <| text <| timeToDateString stats.establishedAt timeZone - ] - , wrappedRow [ width fill ] - [ withTooltip "Timestamp of last time block was received from node if ever" <| el [ alignLeft ] <| text <| "Last Block Received" - , el [ alignRight ] <| text <| maybeTimeToDateString stats.lastBlockReceived timeZone - ] - , wrappedRow [ width fill ] - [ withTooltip "Timestamp of last time fragment was received from node if ever" <| el [ alignLeft ] <| text <| "Last Fragment Received" - , el [ alignRight ] <| text <| maybeTimeToDateString stats.lastFragmentReceived timeZone - ] - , wrappedRow [ width fill ] - [ withTooltip "Timestamp of last time gossip was received from node if ever" <| el [ alignLeft ] <| text <| "Last Fragment Received" - , el [ alignRight ] <| text <| maybeTimeToDateString stats.lastGossipReceived timeZone + , if showNetworkStats then + column + [ width fill + , spacing 10 + , padding 10 + , scrollbarY + , height (fill |> maximum 400) + ] + (List.map + (\stats -> + column [ width fill, padding 20, Background.color (Element.rgb255 240 241 242), Border.solid, Border.color (Element.rgb255 198 205 214), Border.widthEach { bottom = 0, left = 2, right = 0, top = 0 } ] + [ wrappedRow [ width fill ] + [ withTooltip "Hex-encoded node ID" <| el [ alignLeft ] <| text <| "Node ID" + , el [ alignRight ] <| text <| stats.nodeId + ] + , wrappedRow [ width fill ] + [ withTooltip "Timestamp from when the connection was established at" <| el [ alignLeft ] <| text <| "Established At" + , el [ alignRight ] <| text <| timeToDateString stats.establishedAt timeZone + ] + , wrappedRow [ width fill ] + [ withTooltip "Timestamp of last time block was received from node if ever" <| el [ alignLeft ] <| text <| "Last Block Received" + , el [ alignRight ] <| text <| maybeTimeToDateString stats.lastBlockReceived timeZone + ] + , wrappedRow [ width fill ] + [ withTooltip "Timestamp of last time fragment was received from node if ever" <| el [ alignLeft ] <| text <| "Last Fragment Received" + , el [ alignRight ] <| text <| maybeTimeToDateString stats.lastFragmentReceived timeZone + ] + , wrappedRow [ width fill ] + [ withTooltip "Timestamp of last time gossip was received from node if ever" <| el [ alignLeft ] <| text <| "Last Fragment Received" + , el [ alignRight ] <| text <| maybeTimeToDateString stats.lastGossipReceived timeZone + ] ] - ] + ) + networkStats ) - networkStats - ) - else - Element.none - ] + else + Element.none + ] -viewNodeSettings : NodeSettings -> Bool -> Time.Zone -> List (Element Msg) -viewNodeSettings nodeSettings showNodeSettings timeZone = - [ row [ width fill, Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, paddingXY 2 10 ] - [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Node Settings" - , Input.button toggleButtonStyle { onPress = Just ToggleNodeSettingsPanel, label = toggleIcon showNodeSettings } +viewNodeSettings : Bool -> Time.Zone -> NodeSettings -> Element Msg +viewNodeSettings showNodeSettings timeZone nodeSettings = + column + [ Border.color (Element.rgb255 198 205 214) + , Border.width 2 + , padding 10 + , spacing 5 + , alignTop + , width fill ] - , if showNodeSettings then - column [ width fill, spacing 10, padding 10 ] - [ wrappedRow [ width fill ] - [ withTooltip "Hex-encoded hash of block0" <| el [ alignLeft ] <| text <| "Blocks Hash" - , el [ alignRight ] <| text <| nodeSettings.block0Hash - ] - , wrappedRow [ width fill ] - [ withTooltip "When block0 was created" <| el [ alignLeft ] <| text <| "Block Time" - , el [ alignRight ] <| text <| timeToDateString nodeSettings.block0Time timeZone - ] - , wrappedRow [ width fill ] - [ withTooltip "The block content's max size in bytes" <| el [ alignLeft ] <| text <| "Max Block Content" - , el [ alignRight ] <| text <| String.fromInt nodeSettings.blockContentMaxSize - ] - , wrappedRow [ width fill ] - [ withTooltip "Version of consensus, which is currently used" <| el [ alignLeft ] <| text <| "Consensus Version" - , el [ alignRight ] <| text <| nodeSettings.consensusVersion - ] - , wrappedRow [ width fill ] - [ withTooltip "When current slot was opened, not set if none is currently open" <| el [ alignLeft ] <| text <| "Current Slot Start Time" - , el [ alignRight ] <| text <| maybeTimeToDateString nodeSettings.currSlotStartTime timeZone - ] - , wrappedRow [ width fill ] - [ withTooltip "The depth, number of blocks, to which we consider the blockchain to be stable and prevent rollback beyond that depth" <| el [ alignLeft ] <| text <| "Stability Depth" - , el [ alignRight ] <| text <| String.fromInt nodeSettings.epochStabilityDepth - ] - , column [ width fill ] - [ withTooltip "Linear fees configuration" <| el [ alignLeft ] <| text <| "Fees" - , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsFees nodeSettings.fees) - ] - , column [ width fill ] - [ withTooltip "Parameters for rewards calculation" <| el [ alignLeft ] <| text <| "Rewards Parameters" - , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsRewardParams nodeSettings.rewardParams) - ] - , wrappedRow [ width fill ] - [ withTooltip "Slot duration in seconds" <| el [ alignLeft ] <| text <| "Slot Duration" - , el [ alignRight ] <| text <| String.fromInt nodeSettings.slotDuration - ] - , wrappedRow [ width fill ] - [ withTooltip "Number of slots per epoch" <| el [ alignLeft ] <| text <| "Slots Per Epoch" - , el [ alignRight ] <| text <| String.fromInt nodeSettings.slotsPerEpoch - ] - , column [ width fill ] - [ withTooltip "Tax from reward that goes to pot" <| el [ alignLeft ] <| text <| "Treasury Tax" - , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsTreasuryTax nodeSettings.treasuryTax) - ] + [ row [ width fill, Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, paddingXY 2 10 ] + [ el [ alignLeft, fontNormal, Font.size 24 ] <| text <| "Node Settings" + , Input.button toggleButtonStyle { onPress = Just ToggleNodeSettingsPanel, label = toggleIcon showNodeSettings } ] + , if showNodeSettings then + column [ width fill, spacing 10, padding 10 ] + [ wrappedRow [ width fill ] + [ withTooltip "Hex-encoded hash of block0" <| el [ alignLeft ] <| text <| "Blocks Hash" + , el [ alignRight ] <| text <| nodeSettings.block0Hash + ] + , wrappedRow [ width fill ] + [ withTooltip "When block0 was created" <| el [ alignLeft ] <| text <| "Block Time" + , el [ alignRight ] <| text <| timeToDateString nodeSettings.block0Time timeZone + ] + , wrappedRow [ width fill ] + [ withTooltip "The block content's max size in bytes" <| el [ alignLeft ] <| text <| "Max Block Content" + , el [ alignRight ] <| text <| String.fromInt nodeSettings.blockContentMaxSize + ] + , wrappedRow [ width fill ] + [ withTooltip "Version of consensus, which is currently used" <| el [ alignLeft ] <| text <| "Consensus Version" + , el [ alignRight ] <| text <| nodeSettings.consensusVersion + ] + , wrappedRow [ width fill ] + [ withTooltip "When current slot was opened, not set if none is currently open" <| el [ alignLeft ] <| text <| "Current Slot Start Time" + , el [ alignRight ] <| text <| maybeTimeToDateString nodeSettings.currSlotStartTime timeZone + ] + , wrappedRow [ width fill ] + [ withTooltip "The depth, number of blocks, to which we consider the blockchain to be stable and prevent rollback beyond that depth" <| el [ alignLeft ] <| text <| "Stability Depth" + , el [ alignRight ] <| text <| String.fromInt nodeSettings.epochStabilityDepth + ] + , column [ width fill ] + [ withTooltip "Linear fees configuration" <| el [ alignLeft ] <| text <| "Fees" + , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsFees nodeSettings.fees) + ] + , column [ width fill ] + [ withTooltip "Parameters for rewards calculation" <| el [ alignLeft ] <| text <| "Rewards Parameters" + , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsRewardParams nodeSettings.rewardParams) + ] + , wrappedRow [ width fill ] + [ withTooltip "Slot duration in seconds" <| el [ alignLeft ] <| text <| "Slot Duration" + , el [ alignRight ] <| text <| String.fromInt nodeSettings.slotDuration + ] + , wrappedRow [ width fill ] + [ withTooltip "Number of slots per epoch" <| el [ alignLeft ] <| text <| "Slots Per Epoch" + , el [ alignRight ] <| text <| String.fromInt nodeSettings.slotsPerEpoch + ] + , column [ width fill ] + [ withTooltip "Tax from reward that goes to pot" <| el [ alignLeft ] <| text <| "Treasury Tax" + , column [ width fill, spacing 10, padding 10 ] (viewNodeSettingsTreasuryTax nodeSettings.treasuryTax) + ] + ] - else - Element.none - ] + else + Element.none + ] viewNodeSettingsFees : NodeSettingsFees -> List (Element Msg) @@ -2259,8 +2008,8 @@ viewStakePoolsSimple stakePoolsList = stakePoolsList -viewStakeDistribution : StakeDistribution -> Maybe StakePool -> String -> String -> Bool -> Bool -> Bool -> List (Element Msg) -viewStakeDistribution stakeDistribution stakePoolDetail stakePoolID myStakePoolID showZeroStaked sortDescending showSuggestions = +viewStakeDistribution : WebData StakePool -> String -> String -> Bool -> Bool -> Bool -> StakeDistribution -> Element Msg +viewStakeDistribution stakePoolDetail stakePoolID myStakePoolID showZeroStaked sortDescending showSuggestions stakeDistribution = let stakeDetail = stakeDistribution.stake @@ -2285,78 +2034,92 @@ viewStakeDistribution stakeDistribution stakePoolDetail stakePoolID myStakePoolI else withTooltip "Invalid Stake Pool ID Length" <| el [ width fill ] <| text "See on Explorer" in - [ column [ width fill, spacing 10 ] - [ row [ Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, width fill, paddingXY 2 10 ] - [ el [ alignLeft, fontNormal, Font.size 32 ] <| text <| "Stake Distribution" - ] + row + [ Font.size 14 + , fontMono + , spacing 15 + , padding 10 + , width fill ] - , wrappedRow [ width fill, padding 10, spacing 30 ] - [ column [ width (fillPortion 2), spacing 10, padding 10, alignTop, fontMono, Border.widthEach { bottom = 0, left = 0, right = 1, top = 0 }, Font.size 14 ] - [ row [ width fill ] - [ el [ alignLeft ] <| text <| "Stake Pools Staking" - , el [ alignRight ] <| text <| String.fromInt stakedPools - ] - , row [ width fill ] - [ el [ alignLeft ] <| text <| "Total Stake Pools" - , el [ alignRight ] <| text <| String.fromInt totalPools - ] - , column [ width fill, spacing 10 ] - [ row [ width fill ] - [ withTooltip "Epoch of last block" <| el [ alignLeft ] <| text <| "Epoch" - , el [ alignRight ] <| text <| String.fromInt stakeDistribution.epoch + -- [ case model.stakePoolsList of + -- Just stakePoolsList -> + -- viewPanel (viewStakePools stakePoolsList model.showStakePoolsList) + -- Nothing -> + -- Element.none + [ column [ centerX, width fill ] + [ column [ width fill, spacing 10 ] + [ row [ Border.widthEach { bottom = 1, left = 0, right = 0, top = 0 }, width fill, paddingXY 2 10 ] + [ el [ alignLeft, fontNormal, Font.size 32 ] <| text <| "Stake Distribution" ] - , column [ width fill, spacing 10 ] + ] + , wrappedRow [ width fill, padding 10, spacing 30 ] + [ column [ width (fillPortion 2), spacing 10, padding 10, alignTop, fontMono, Border.widthEach { bottom = 0, left = 0, right = 1, top = 0 }, Font.size 14 ] [ row [ width fill ] - [ withTooltip "Total value stored in accounts, but assigned to nonexistent pools" <| el [ alignLeft ] <| text <| "Dangling" - , row [ alignRight, spacing 5 ] [ adaIcon, el [ alignRight ] <| text <| formatAdaValue stakeDetail.dangling ] + [ el [ alignLeft ] <| text <| "Stake Pools Staking" + , el [ alignRight ] <| text <| String.fromInt stakedPools ] , row [ width fill ] - [ withTooltip "Total value stored in accounts, but not assigned to any pool" <| el [ alignLeft ] <| text <| "Unassigned" - , row [ alignRight, spacing 5 ] [ adaIcon, el [ alignRight ] <| text <| formatAdaValue stakeDetail.unassigned ] + [ el [ alignLeft ] <| text <| "Total Stake Pools" + , el [ alignRight ] <| text <| String.fromInt totalPools ] - , row [ width fill ] - [ withTooltip "Total value staked between pools" <| el [ width fill, alignLeft ] <| text <| "Total Staked" - , row [ alignRight, spacing 5 ] [ adaIcon, el [] <| text <| formatAdaValue stakedValue ] - ] - , row [ width fill ] - [ withTooltip "Total value in circulation" <| el [ width fill, alignLeft ] <| text <| "Total" - , row [ alignRight, spacing 5 ] [ adaIcon, el [] <| text <| formatAdaValue totalValue ] - ] - ] - ] - , column [ Border.widthEach { bottom = 0, left = 0, right = 0, top = 1 }, width fill, paddingEach { bottom = 0, left = 0, right = 0, top = 10 }, spacing 10 ] - [ if showSuggestions then - row [ width fill, spacing 5 ] - [ Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput, inputSuggestions stakePoolID (List.map (\pool -> pool.id) stakeDistribution.stake.pools) StakePoolIdClicked ] - { onChange = StakePoolIdChanged - , text = stakePoolID - , placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")) - , label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") - } - , cardanoExplorerLink + , column [ width fill, spacing 10 ] + [ row [ width fill ] + [ withTooltip "Epoch of last block" <| el [ alignLeft ] <| text <| "Epoch" + , el [ alignRight ] <| text <| String.fromInt stakeDistribution.epoch + ] + , column [ width fill, spacing 10 ] + [ row [ width fill ] + [ withTooltip "Total value stored in accounts, but assigned to nonexistent pools" <| el [ alignLeft ] <| text <| "Dangling" + , row [ alignRight, spacing 5 ] [ adaIcon, el [ alignRight ] <| text <| formatAdaValue stakeDetail.dangling ] + ] + , row [ width fill ] + [ withTooltip "Total value stored in accounts, but not assigned to any pool" <| el [ alignLeft ] <| text <| "Unassigned" + , row [ alignRight, spacing 5 ] [ adaIcon, el [ alignRight ] <| text <| formatAdaValue stakeDetail.unassigned ] + ] + , row [ width fill ] + [ withTooltip "Total value staked between pools" <| el [ width fill, alignLeft ] <| text <| "Total Staked" + , row [ alignRight, spacing 5 ] [ adaIcon, el [] <| text <| formatAdaValue stakedValue ] + ] + , row [ width fill ] + [ withTooltip "Total value in circulation" <| el [ width fill, alignLeft ] <| text <| "Total" + , row [ alignRight, spacing 5 ] [ adaIcon, el [] <| text <| formatAdaValue totalValue ] + ] + ] ] + , column [ Border.widthEach { bottom = 0, left = 0, right = 0, top = 1 }, width fill, paddingEach { bottom = 0, left = 0, right = 0, top = 10 }, spacing 10 ] + [ if showSuggestions then + row [ width fill, spacing 5 ] + [ Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput, inputSuggestions stakePoolID (List.map (\pool -> pool.id) stakeDistribution.stake.pools) StakePoolIdClicked ] + { onChange = StakePoolIdChanged + , text = stakePoolID + , placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")) + , label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") + } + , cardanoExplorerLink + ] - else - row [ width fill, spacing 5 ] - [ Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput ] - { onChange = StakePoolIdChanged - , text = stakePoolID - , placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")) - , label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") - } - , cardanoExplorerLink - ] - , case stakePoolDetail of - Just spDetail -> - viewStakePoolDetail spDetail + else + row [ width fill, spacing 5 ] + [ Input.search [ centerY, Events.onFocus ActivateStakePoolInput, Events.onLoseFocus DeactivateStakePoolInput ] + { onChange = StakePoolIdChanged + , text = stakePoolID + , placeholder = Maybe.Just (Input.placeholder [ centerY ] (text "Click a Stake Pool ID")) + , label = Input.labelLeft [ centerY ] (text "Stake Pool ID: ") + } + , cardanoExplorerLink + ] + , case stakePoolDetail of + NotAsked -> + el [ centerX ] <| text <| "Click a Stake Pool ID to see details " - Nothing -> - el [ centerX ] <| text <| "Click a Stake Pool ID to see details " + _ -> + webDataView viewStakePoolDetail stakePoolDetail + ] + ] + , viewStakePoolsTable stakeDistribution.stake.pools showZeroStaked sortDescending myStakePoolID stakedValue ] ] - , viewStakePoolsTable stakeDistribution.stake.pools showZeroStaked sortDescending myStakePoolID stakedValue ] - ] inputSuggestions : String -> List String -> (String -> Msg) -> Element.Attribute Msg @@ -2599,8 +2362,8 @@ viewLeaders leadersList = leadersList -viewLeadersLogs : Posix -> List LeadersLogs -> Time.Zone -> Element Msg -viewLeadersLogs time leadersLogsList timeZone = +viewLeadersLogs : Posix -> Time.Zone -> List LeadersLogs -> Element Msg +viewLeadersLogs time timeZone leadersLogsList = let leadersLogsSorted = List.sortBy (\leader -> Time.posixToMillis leader.scheduled_at_time) leadersLogsList @@ -3057,6 +2820,11 @@ leadersSvg = loadingSvg : Element msg loadingSvg = + Element.html <| svg [ SvgA.width "122", SvgA.height "122", SvgA.viewBox "0 0 122 122", SvgA.version "1.1" ] [ g [ SvgA.id "Page-1", SvgA.stroke "none", SvgA.strokeWidth "1", SvgA.fill "none", SvgA.fillRule "evenodd" ] [ g [ SvgA.id "styles", SvgA.transform "translate(-860.000000, -5380.000000)", SvgA.fill "#FAFBFC" ] [ g [ SvgA.id "spinner-light", SvgA.transform "translate(860.000000, 5380.000000)" ] [ Svg.path [ SvgA.d "M18.5735931,18.5735931 C-4.85786437,42.0050506 -4.85786437,79.9949494 18.5735931,103.426407 C42.0050506,126.857864 79.9949494,126.857864 103.426407,103.426407 L97.7695526,97.7695526 C77.4622895,118.076816 44.5377105,118.076816 24.2304474,97.7695526 C3.92318421,77.4622895 3.92318421,44.5377105 24.2304474,24.2304474 L18.5735931,18.5735931 L18.5735931,18.5735931 L18.5735931,18.5735931 Z", SvgA.id "spinner" ] [] ] ] ] ] + + +fetchingSvg : Element msg +fetchingSvg = Element.html <| svg [ SvgA.width "30", SvgA.height "30", SvgA.viewBox "0 0 122 122", SvgA.version "1.1" ] [ g [ SvgA.id "Page-1", SvgA.stroke "none", SvgA.strokeWidth "1", SvgA.fill "none", SvgA.fillRule "evenodd" ] [ g [ SvgA.id "styles", SvgA.transform "translate(-860.000000, -5380.000000)", SvgA.fill "#FAFBFC" ] [ g [ SvgA.id "spinner-light", SvgA.transform "translate(860.000000, 5380.000000)" ] [ Svg.path [ SvgA.d "M18.5735931,18.5735931 C-4.85786437,42.0050506 -4.85786437,79.9949494 18.5735931,103.426407 C42.0050506,126.857864 79.9949494,126.857864 103.426407,103.426407 L97.7695526,97.7695526 C77.4622895,118.076816 44.5377105,118.076816 24.2304474,97.7695526 C3.92318421,77.4622895 3.92318421,44.5377105 24.2304474,24.2304474 L18.5735931,18.5735931 L18.5735931,18.5735931 L18.5735931,18.5735931 Z", SvgA.id "spinner" ] [] ] ] ] ] -- GitLab