diff --git a/src/elm/Components/Logs.elm b/src/elm/Components/Logs.elm index 56652f6c3..0b7ad159c 100644 --- a/src/elm/Components/Logs.elm +++ b/src/elm/Components/Logs.elm @@ -142,14 +142,14 @@ viewLine shared props logLine lineNumber = [ div [ class "wrapper" , Util.testAttribute <| String.join "-" [ "log", "line", props.resourceType, props.resourceNumber, String.fromInt lineNumber ] - , class <| Focus.lineRangeStyles props.focus lineNumber + , class <| Focus.lineRangeStyles (String.toInt props.resourceNumber) lineNumber props.focus ] [ td [] [ button [ Util.onClickPreventDefault <| props.msgs.pushUrlHash { hash = - Focus.toString <| Focus.updateLineRange shared props.focus lineNumber + Focus.toString <| Focus.updateLineRange shared (String.toInt props.resourceNumber) lineNumber props.focus } , Util.testAttribute <| String.join "-" [ "log", "line", "num", props.resourceType, props.resourceNumber, String.fromInt lineNumber ] , Focus.toAttr diff --git a/src/elm/Pages/Org_/Repo_/Build_.elm b/src/elm/Pages/Org_/Repo_/Build_.elm index 17c427278..ddff6a3ea 100644 --- a/src/elm/Pages/Org_/Repo_/Build_.elm +++ b/src/elm/Pages/Org_/Repo_/Build_.elm @@ -6,6 +6,7 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Org_.Repo_.Build_ exposing (..) import Auth +import Browser.Dom exposing (focus) import Components.Logs import Components.Svgs import Debug exposing (log) @@ -13,21 +14,22 @@ import Dict exposing (Dict) import Effect exposing (Effect) import FeatherIcons import Html exposing (Html, button, code, details, div, small, summary, text) -import Html.Attributes exposing (attribute, class, classList, id) +import Html.Attributes exposing (attribute, class, classList) import Html.Events exposing (onClick) import Http import Http.Detailed import Layouts import List.Extra -import Maybe.Extra import Page exposing (Page) import RemoteData exposing (WebData) import Route exposing (Route) import Route.Path import Shared +import Time import Utils.Errors import Utils.Focus as Focus import Utils.Helpers as Util +import Utils.Interval as Interval exposing (Interval) import Vela import View exposing (View) @@ -73,6 +75,7 @@ toLayout user route model = type alias Model = { steps : WebData (List Vela.Step) , logs : Dict Int (WebData Vela.Log) + , viewing : List Int , focus : Focus.Focus , logFollow : Int } @@ -82,6 +85,7 @@ init : Shared.Model -> Route { org : String, repo : String, buildNumber : String init shared route () = ( { steps = RemoteData.Loading , logs = Dict.empty + , viewing = [] , focus = Focus.fromString route.hash , logFollow = 0 } @@ -103,41 +107,48 @@ init shared route () = type Msg - = -- BROWSER + = NoOp + | -- BROWSER OnHashChanged { from : Maybe String, to : Maybe String } | PushUrlHash { hash : String } | FocusOn { target : String } -- STEPS | GetBuildStepsResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Step )) - | GetBuildStepLogResponse Vela.Step (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Log )) - | ExpandStep { step : Vela.Step, updateUrlHash : Bool } - | CollapseStep { step : Vela.Step, updateUrlHash : Bool } + | GetBuildStepsRefreshResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Step )) + | GetBuildStepLogResponse { step : Vela.Step, applyDomFocus : Bool, previousFocus : Maybe Focus.Focus } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Log )) + | GetBuildStepLogRefreshResponse { step : Vela.Step } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Log )) + | ClickStep { step : Vela.Step } + | ExpandStep { step : Vela.Step, applyDomFocus : Bool, previousFocus : Maybe Focus.Focus } + | CollapseStep { step : Vela.Step } | ExpandAll | CollapseAll -- LOGS | DownloadLog { filename : String, content : String, map : String -> String } | FollowLog { number : Int } + -- REFRESH + | Tick { time : Time.Posix, interval : Interval } update : Shared.Model -> Route { org : String, repo : String, buildNumber : String } -> Msg -> Model -> ( Model, Effect Msg ) update shared route msg model = case msg of + NoOp -> + ( model, Effect.none ) + -- BROWSER OnHashChanged options -> let focus = - Focus.fromString route.hash + Focus.fromString options.to in - ( { model | focus = focus } - , model.steps - |> RemoteData.unwrap Effect.none - (\steps -> - steps - |> List.Extra.find (\s -> s.number == Maybe.withDefault -1 focus.group) - |> Maybe.map (\s -> ExpandStep { step = s, updateUrlHash = False }) - |> Maybe.map Effect.sendMsg - |> Maybe.withDefault Effect.none - ) + ( { model + | focus = focus + } + , RemoteData.withDefault [] model.steps + |> List.filter (\s -> Maybe.withDefault -1 focus.group == s.number) + |> List.map (\s -> ExpandStep { step = s, applyDomFocus = True, previousFocus = Just model.focus }) + |> List.map Effect.sendMsg + |> Effect.batch ) PushUrlHash options -> @@ -161,41 +172,20 @@ update shared route msg model = GetBuildStepsResponse response -> case response of Ok ( _, steps ) -> - let - ( steps_, sideEffects ) = - steps - |> List.map - (\step -> - case model.focus.group of - Just resourceNumber -> - if step.number == resourceNumber then - ( { step | viewing = True } - , Effect.batch - [ ExpandStep { step = step, updateUrlHash = False } - |> Effect.sendMsg - , FocusOn - { target = - Focus.toDomTarget model.focus - } - |> Effect.sendMsg - ] - ) - - else - ( { step | viewing = False }, Effect.none ) - - _ -> - ( { step | viewing = False }, Effect.none ) - ) - |> List.unzip - |> Tuple.mapFirst RemoteData.succeed - |> Tuple.mapSecond Effect.batch - in - ( { model - | steps = - steps_ - } - , sideEffects + ( { model | steps = RemoteData.succeed steps } + , steps + |> List.Extra.find (\step -> Maybe.withDefault -1 model.focus.group == step.number) + |> Maybe.map (\step -> step) + |> Maybe.map + (\step -> + ExpandStep + { step = step + , applyDomFocus = True + , previousFocus = Nothing + } + |> Effect.sendMsg + ) + |> Maybe.withDefault Effect.none ) Err error -> @@ -203,18 +193,67 @@ update shared route msg model = , Effect.handleHttpError { httpError = error } ) - GetBuildStepLogResponse step response -> + GetBuildStepsRefreshResponse response -> + case response of + Ok ( _, steps ) -> + ( { model | steps = RemoteData.succeed steps } + , steps + |> List.filter (\step -> List.member step.number model.viewing) + |> List.map + (\step -> + Effect.getBuildStepLog + { baseUrl = shared.velaAPI + , session = shared.session + , onResponse = GetBuildStepLogRefreshResponse { step = step } + , org = route.params.org + , repo = route.params.repo + , buildNumber = route.params.buildNumber + , stepNumber = String.fromInt step.number + } + ) + |> Effect.batch + ) + + Err error -> + ( { model | steps = Utils.Errors.toFailure error } + , Effect.handleHttpError { httpError = error } + ) + + GetBuildStepLogResponse options response -> case response of Ok ( _, log ) -> ( { model | logs = - Dict.update step.id + Dict.update options.step.id (Components.Logs.safeDecodeLogData shared.velaLogBytesLimit log) model.logs } - , if Focus.canTarget model.focus then - FocusOn { target = Focus.toDomTarget model.focus } - |> Effect.sendMsg + , if options.applyDomFocus then + case ( model.focus.group, model.focus.a, model.focus.b ) of + ( Just g, Just _, Just _ ) -> + FocusOn + { target = + Focus.toDomTarget + { group = Just g + , a = Focus.lineNumberChanged options.previousFocus model.focus + , b = Nothing + } + } + |> Effect.sendMsg + + ( Just g, Just a, _ ) -> + FocusOn + { target = + Focus.toDomTarget + { group = Just g + , a = Just a + , b = Nothing + } + } + |> Effect.sendMsg + + _ -> + Effect.none else Effect.none @@ -225,47 +264,79 @@ update shared route msg model = , Effect.handleHttpError { httpError = error } ) + GetBuildStepLogRefreshResponse options response -> + case response of + Ok ( _, log ) -> + ( { model + | logs = + Dict.update options.step.id + (Components.Logs.safeDecodeLogData shared.velaLogBytesLimit log) + model.logs + } + , Effect.none + ) + + Err error -> + ( { model | steps = Utils.Errors.toFailure error } + , Effect.handleHttpError { httpError = error } + ) + + ClickStep options -> + ( model + , if List.member options.step.number model.viewing then + CollapseStep { step = options.step } + |> Effect.sendMsg + + else + Effect.batch + [ ExpandStep { step = options.step, applyDomFocus = False, previousFocus = Nothing } + |> Effect.sendMsg + , { hash = + Focus.toString + { group = Just options.step.number + , a = Nothing + , b = Nothing + } + } + |> PushUrlHash + |> Effect.sendMsg + ] + ) + ExpandStep options -> ( { model - | steps = - case model.steps of - RemoteData.Success steps -> - List.Extra.updateIf - (\s -> s.id == options.step.id) - (\s -> { s | viewing = True }) - steps - |> RemoteData.succeed - - _ -> - model.steps + | viewing = List.Extra.unique <| options.step.number :: model.viewing } , Effect.batch [ Effect.getBuildStepLog { baseUrl = shared.velaAPI , session = shared.session - , onResponse = GetBuildStepLogResponse options.step + , onResponse = + GetBuildStepLogResponse + { step = options.step + , applyDomFocus = options.applyDomFocus + , previousFocus = options.previousFocus + } , org = route.params.org , repo = route.params.repo , buildNumber = route.params.buildNumber , stepNumber = String.fromInt options.step.number } - , if options.updateUrlHash then - Effect.pushRoute - { path = - Route.Path.Org_Repo_Build_ - { org = route.params.org - , repo = route.params.repo - , buildNumber = route.params.buildNumber + , if options.applyDomFocus then + case ( model.focus.group, model.focus.a, model.focus.b ) of + ( Just g, Nothing, Nothing ) -> + FocusOn + { target = + Focus.toDomTarget + { group = Just g + , a = Nothing + , b = Nothing + } } - , query = route.query - , hash = - Just <| - Focus.toString - { group = Just options.step.number - , a = Nothing - , b = Nothing - } - } + |> Effect.sendMsg + + _ -> + Effect.none else Effect.none @@ -274,17 +345,7 @@ update shared route msg model = CollapseStep options -> ( { model - | steps = - case model.steps of - RemoteData.Success steps -> - List.Extra.updateIf - (\s -> s.id == options.step.id) - (\s -> { s | viewing = False }) - steps - |> RemoteData.succeed - - _ -> - model.steps + | viewing = List.Extra.remove options.step.number model.viewing } , Effect.none ) @@ -293,7 +354,14 @@ update shared route msg model = ( model , model.steps |> RemoteData.withDefault [] - |> List.map (\step -> ExpandStep { step = step, updateUrlHash = False }) + |> List.map + (\step -> + ExpandStep + { step = step + , applyDomFocus = False + , previousFocus = Nothing + } + ) |> List.map Effect.sendMsg |> Effect.batch ) @@ -302,7 +370,7 @@ update shared route msg model = ( model , model.steps |> RemoteData.withDefault [] - |> List.map (\step -> CollapseStep { step = step, updateUrlHash = False }) + |> List.map (\step -> CollapseStep { step = step }) |> List.map Effect.sendMsg |> Effect.batch ) @@ -318,6 +386,21 @@ update shared route msg model = , Effect.none ) + -- REFRESH + Tick options -> + ( model + , Effect.getBuildSteps + { baseUrl = shared.velaAPI + , session = shared.session + , onResponse = GetBuildStepsRefreshResponse + , pageNumber = Nothing + , perPage = Just 100 + , org = route.params.org + , repo = route.params.repo + , buildNumber = route.params.buildNumber + } + ) + -- SUBSCRIPTIONS @@ -325,7 +408,7 @@ update shared route msg model = subscriptions : Model -> Sub Msg subscriptions model = - Sub.none + Interval.tickEveryFiveSeconds Tick @@ -385,17 +468,6 @@ view shared route model = viewStep : Shared.Model -> Model -> Route { org : String, repo : String, buildNumber : String } -> Vela.Step -> Html Msg viewStep shared model route step = - let - stepNumber = - String.fromInt step.number - - clickStep = - if step.viewing then - CollapseStep - - else - ExpandStep - in div [ classList [ ( "step", True ) @@ -411,12 +483,12 @@ viewStep shared model route step = , ( "-with-border", True ) , ( "-running", step.status == Vela.Running ) ] - :: Util.open step.viewing + :: Util.open (List.member step.number model.viewing) ) [ summary [ class "summary" - , Util.testAttribute <| "step-header-" ++ stepNumber - , onClick <| clickStep { step = step, updateUrlHash = True } + , Util.testAttribute <| "step-header-" ++ String.fromInt step.number + , onClick <| ClickStep { step = step } , Focus.toAttr { group = Just step.number , a = Nothing diff --git a/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm b/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm index 5b8fb30c1..6c73cc830 100644 --- a/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm +++ b/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm @@ -200,11 +200,17 @@ update shared route msg model = route.hash |> Focus.fromStringNoGroup in ( { model - | focus = - focus + | focus = focus } , if Focus.canTarget focus then - FocusOn { target = Focus.toDomTarget focus } + FocusOn + { target = + Focus.toDomTarget + { group = focus.group + , a = Focus.lineNumberChanged (Just model.focus) focus + , b = Nothing + } + } |> Effect.sendMsg else @@ -535,14 +541,14 @@ viewLine shared lineNumber line focus = div [ class "wrapper" , Util.testAttribute <| String.join "-" [ "config", "line", String.fromInt lineNumber ] - , class <| Focus.lineRangeStyles focus lineNumber + , class <| Focus.lineRangeStyles Nothing lineNumber focus ] [ td [] [ button [ Util.onClickPreventDefault <| PushUrlHash { hash = - Focus.toString <| Focus.updateLineRange shared focus lineNumber + Focus.toString <| Focus.updateLineRange shared Nothing lineNumber focus } , Util.testAttribute <| String.join "-" [ "config", "line", "num", String.fromInt lineNumber ] , Focus.toAttr diff --git a/src/elm/Pages/Org_/Repo_/Build_/Services.elm b/src/elm/Pages/Org_/Repo_/Build_/Services.elm index 545dcb3ce..d6dd76726 100644 --- a/src/elm/Pages/Org_/Repo_/Build_/Services.elm +++ b/src/elm/Pages/Org_/Repo_/Build_/Services.elm @@ -6,6 +6,7 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Org_.Repo_.Build_.Services exposing (..) import Auth +import Browser.Dom exposing (focus) import Components.Logs import Components.Svgs import Debug exposing (log) @@ -13,7 +14,7 @@ import Dict exposing (Dict) import Effect exposing (Effect) import FeatherIcons import Html exposing (Html, button, code, details, div, small, summary, text) -import Html.Attributes exposing (attribute, class, classList, id) +import Html.Attributes exposing (attribute, class, classList) import Html.Events exposing (onClick) import Http import Http.Detailed @@ -24,9 +25,11 @@ import RemoteData exposing (WebData) import Route exposing (Route) import Route.Path import Shared +import Time import Utils.Errors import Utils.Focus as Focus import Utils.Helpers as Util +import Utils.Interval as Interval exposing (Interval) import Vela import View exposing (View) @@ -72,6 +75,7 @@ toLayout user route model = type alias Model = { services : WebData (List Vela.Service) , logs : Dict Int (WebData Vela.Log) + , viewing : List Int , focus : Focus.Focus , logFollow : Int } @@ -81,6 +85,7 @@ init : Shared.Model -> Route { org : String, repo : String, buildNumber : String init shared route () = ( { services = RemoteData.Loading , logs = Dict.empty + , viewing = [] , focus = Focus.fromString route.hash , logFollow = 0 } @@ -89,7 +94,7 @@ init shared route () = , session = shared.session , onResponse = GetBuildServicesResponse , pageNumber = Nothing - , perPage = Nothing + , perPage = Just 100 , org = route.params.org , repo = route.params.repo , buildNumber = route.params.buildNumber @@ -102,48 +107,55 @@ init shared route () = type Msg - = -- BROWSER + = NoOp + | -- BROWSER OnHashChanged { from : Maybe String, to : Maybe String } | PushUrlHash { hash : String } | FocusOn { target : String } -- SERVICES | GetBuildServicesResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Service )) - | GetBuildServiceLogResponse Vela.Service (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Log )) - | ExpandService { service : Vela.Service, updateUrlHash : Bool } - | CollapseService { service : Vela.Service, updateUrlHash : Bool } + | GetBuildServicesRefreshResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Service )) + | GetBuildServiceLogResponse { service : Vela.Service, applyDomFocus : Bool, previousFocus : Maybe Focus.Focus } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Log )) + | GetBuildServiceLogRefreshResponse { service : Vela.Service } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Log )) + | ClickService { service : Vela.Service } + | ExpandService { service : Vela.Service, applyDomFocus : Bool, previousFocus : Maybe Focus.Focus } + | CollapseService { service : Vela.Service } | ExpandAll | CollapseAll -- LOGS | DownloadLog { filename : String, content : String, map : String -> String } | FollowLog { number : Int } + -- REFRESH + | Tick { time : Time.Posix, interval : Interval } update : Shared.Model -> Route { org : String, repo : String, buildNumber : String } -> Msg -> Model -> ( Model, Effect Msg ) update shared route msg model = case msg of + NoOp -> + ( model, Effect.none ) + -- BROWSER - OnHashChanged _ -> + OnHashChanged options -> let focus = - Focus.fromString route.hash + Focus.fromString options.to in - ( { model | focus = focus } - , model.services - |> RemoteData.unwrap Effect.none - (\services -> - services - |> List.Extra.find (\s -> s.number == Maybe.withDefault -1 focus.group) - |> Maybe.map (\s -> ExpandService { service = s, updateUrlHash = False }) - |> Maybe.map Effect.sendMsg - |> Maybe.withDefault Effect.none - ) + ( { model + | focus = focus + } + , RemoteData.withDefault [] model.services + |> List.filter (\s -> Maybe.withDefault -1 focus.group == s.number) + |> List.map (\s -> ExpandService { service = s, applyDomFocus = True, previousFocus = Just model.focus }) + |> List.map Effect.sendMsg + |> Effect.batch ) PushUrlHash options -> ( model , Effect.pushRoute { path = - Route.Path.Org_Repo_Build_Services + Route.Path.Org_Repo_Build_ { org = route.params.org , repo = route.params.repo , buildNumber = route.params.buildNumber @@ -160,34 +172,20 @@ update shared route msg model = GetBuildServicesResponse response -> case response of Ok ( _, services ) -> - let - ( services_, sideEffects ) = - services - |> List.map - (\service -> - case model.focus.group of - Just resourceNumber -> - if service.number == resourceNumber then - ( { service | viewing = True } - , ExpandService { service = service, updateUrlHash = False } - |> Effect.sendMsg - ) - - else - ( { service | viewing = False }, Effect.none ) - - _ -> - ( { service | viewing = False }, Effect.none ) - ) - |> List.unzip - |> Tuple.mapFirst RemoteData.succeed - |> Tuple.mapSecond Effect.batch - in - ( { model - | services = - services_ - } - , sideEffects + ( { model | services = RemoteData.succeed services } + , services + |> List.Extra.find (\service -> Maybe.withDefault -1 model.focus.group == service.number) + |> Maybe.map (\service -> service) + |> Maybe.map + (\service -> + ExpandService + { service = service + , applyDomFocus = True + , previousFocus = Nothing + } + |> Effect.sendMsg + ) + |> Maybe.withDefault Effect.none ) Err error -> @@ -195,18 +193,67 @@ update shared route msg model = , Effect.handleHttpError { httpError = error } ) - GetBuildServiceLogResponse service response -> + GetBuildServicesRefreshResponse response -> + case response of + Ok ( _, services ) -> + ( { model | services = RemoteData.succeed services } + , services + |> List.filter (\service -> List.member service.number model.viewing) + |> List.map + (\service -> + Effect.getBuildServiceLog + { baseUrl = shared.velaAPI + , session = shared.session + , onResponse = GetBuildServiceLogRefreshResponse { service = service } + , org = route.params.org + , repo = route.params.repo + , buildNumber = route.params.buildNumber + , serviceNumber = String.fromInt service.number + } + ) + |> Effect.batch + ) + + Err error -> + ( { model | services = Utils.Errors.toFailure error } + , Effect.handleHttpError { httpError = error } + ) + + GetBuildServiceLogResponse options response -> case response of Ok ( _, log ) -> ( { model | logs = - Dict.update service.id + Dict.update options.service.id (Components.Logs.safeDecodeLogData shared.velaLogBytesLimit log) model.logs } - , if Focus.canTarget model.focus then - FocusOn { target = Focus.toDomTarget model.focus } - |> Effect.sendMsg + , if options.applyDomFocus then + case ( model.focus.group, model.focus.a, model.focus.b ) of + ( Just g, Just _, Just _ ) -> + FocusOn + { target = + Focus.toDomTarget + { group = Just g + , a = Focus.lineNumberChanged options.previousFocus model.focus + , b = Nothing + } + } + |> Effect.sendMsg + + ( Just g, Just a, _ ) -> + FocusOn + { target = + Focus.toDomTarget + { group = Just g + , a = Just a + , b = Nothing + } + } + |> Effect.sendMsg + + _ -> + Effect.none else Effect.none @@ -217,47 +264,79 @@ update shared route msg model = , Effect.handleHttpError { httpError = error } ) + GetBuildServiceLogRefreshResponse options response -> + case response of + Ok ( _, log ) -> + ( { model + | logs = + Dict.update options.service.id + (Components.Logs.safeDecodeLogData shared.velaLogBytesLimit log) + model.logs + } + , Effect.none + ) + + Err error -> + ( { model | services = Utils.Errors.toFailure error } + , Effect.handleHttpError { httpError = error } + ) + + ClickService options -> + ( model + , if List.member options.service.number model.viewing then + CollapseService { service = options.service } + |> Effect.sendMsg + + else + Effect.batch + [ ExpandService { service = options.service, applyDomFocus = False, previousFocus = Nothing } + |> Effect.sendMsg + , { hash = + Focus.toString + { group = Just options.service.number + , a = Nothing + , b = Nothing + } + } + |> PushUrlHash + |> Effect.sendMsg + ] + ) + ExpandService options -> ( { model - | services = - case model.services of - RemoteData.Success services -> - List.Extra.updateIf - (\s -> s.id == options.service.id) - (\s -> { s | viewing = True }) - services - |> RemoteData.succeed - - _ -> - model.services + | viewing = List.Extra.unique <| options.service.number :: model.viewing } , Effect.batch [ Effect.getBuildServiceLog { baseUrl = shared.velaAPI , session = shared.session - , onResponse = GetBuildServiceLogResponse options.service + , onResponse = + GetBuildServiceLogResponse + { service = options.service + , applyDomFocus = options.applyDomFocus + , previousFocus = options.previousFocus + } , org = route.params.org , repo = route.params.repo , buildNumber = route.params.buildNumber , serviceNumber = String.fromInt options.service.number } - , if options.updateUrlHash then - Effect.pushRoute - { path = - Route.Path.Org_Repo_Build_Services - { org = route.params.org - , repo = route.params.repo - , buildNumber = route.params.buildNumber + , if options.applyDomFocus then + case ( model.focus.group, model.focus.a, model.focus.b ) of + ( Just g, Nothing, Nothing ) -> + FocusOn + { target = + Focus.toDomTarget + { group = Just g + , a = Nothing + , b = Nothing + } } - , query = route.query - , hash = - Just <| - Focus.toString - { group = Just options.service.number - , a = Nothing - , b = Nothing - } - } + |> Effect.sendMsg + + _ -> + Effect.none else Effect.none @@ -266,17 +345,7 @@ update shared route msg model = CollapseService options -> ( { model - | services = - case model.services of - RemoteData.Success services -> - List.Extra.updateIf - (\s -> s.id == options.service.id) - (\s -> { s | viewing = False }) - services - |> RemoteData.succeed - - _ -> - model.services + | viewing = List.Extra.remove options.service.number model.viewing } , Effect.none ) @@ -285,7 +354,14 @@ update shared route msg model = ( model , model.services |> RemoteData.withDefault [] - |> List.map (\service -> ExpandService { service = service, updateUrlHash = False }) + |> List.map + (\service -> + ExpandService + { service = service + , applyDomFocus = False + , previousFocus = Nothing + } + ) |> List.map Effect.sendMsg |> Effect.batch ) @@ -294,7 +370,7 @@ update shared route msg model = ( model , model.services |> RemoteData.withDefault [] - |> List.map (\service -> CollapseService { service = service, updateUrlHash = False }) + |> List.map (\service -> CollapseService { service = service }) |> List.map Effect.sendMsg |> Effect.batch ) @@ -310,6 +386,21 @@ update shared route msg model = , Effect.none ) + -- REFRESH + Tick options -> + ( model + , Effect.getBuildServices + { baseUrl = shared.velaAPI + , session = shared.session + , onResponse = GetBuildServicesRefreshResponse + , pageNumber = Nothing + , perPage = Just 100 + , org = route.params.org + , repo = route.params.repo + , buildNumber = route.params.buildNumber + } + ) + -- SUBSCRIPTIONS @@ -317,7 +408,7 @@ update shared route msg model = subscriptions : Model -> Sub Msg subscriptions model = - Sub.none + Interval.tickEveryFiveSeconds Tick @@ -326,7 +417,7 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String, buildNumber : String } -> Model -> View Msg view shared route model = - { title = "Services" + { title = "" , body = [ case model.services of RemoteData.Success services -> @@ -372,18 +463,13 @@ view shared route model = viewService : Shared.Model -> Model -> Route { org : String, repo : String, buildNumber : String } -> Vela.Service -> Html Msg viewService shared model route service = - let - serviceNumber = - String.fromInt service.number - - clickService = - if service.viewing then - CollapseService - - else - ExpandService - in - div [ classList [ ( "service", True ), ( "flowline-left", True ) ], Util.testAttribute "service" ] + div + [ classList + [ ( "service", True ) + , ( "flowline-left", True ) + ] + , Util.testAttribute "service" + ] [ div [ class "-status" ] [ div [ class "-icon-container" ] [ Components.Svgs.statusToIcon service.status ] ] , details @@ -392,13 +478,17 @@ viewService shared model route service = , ( "-with-border", True ) , ( "-running", service.status == Vela.Running ) ] - :: Util.open service.viewing + :: Util.open (List.member service.number model.viewing) ) [ summary [ class "summary" - , Util.testAttribute <| "service-header-" ++ serviceNumber - , onClick <| clickService { service = service, updateUrlHash = True } - , Focus.toAttr { group = Just service.number, a = Nothing, b = Nothing } + , Util.testAttribute <| "service-header-" ++ String.fromInt service.number + , onClick <| ClickService { service = service } + , Focus.toAttr + { group = Just service.number + , a = Nothing + , b = Nothing + } ] [ div [ class "-info" ] diff --git a/src/elm/Utils/Focus.elm b/src/elm/Utils/Focus.elm index 7ff001ea7..4eaf0df8b 100644 --- a/src/elm/Utils/Focus.elm +++ b/src/elm/Utils/Focus.elm @@ -9,6 +9,7 @@ module Utils.Focus exposing , fromAttrId , fromString , fromStringNoGroup + , lineNumberChanged , lineRangeStyles , toAttr , toDomTarget @@ -178,8 +179,8 @@ canTarget focus = False -updateLineRange : Shared.Model -> Focus -> Int -> Focus -updateLineRange shared focus lineNumber = +updateLineRange : Shared.Model -> Maybe Int -> Int -> Focus -> Focus +updateLineRange shared group lineNumber focus = (case ( shared.shift, ( focus.a, focus.b ) ) of ( True, ( Just lineA, Just lineB ) ) -> if lineNumber < lineA then @@ -202,29 +203,29 @@ updateLineRange shared focus lineNumber = |> (\range -> case range of a :: b :: [] -> - { focus - | a = Just a - , b = Just b + { group = group + , a = Just a + , b = Just b } a :: [] -> - { focus - | a = Just a - , b = Nothing + { group = group + , a = Just a + , b = Nothing } _ -> - { focus - | a = Nothing - , b = Nothing + { group = group + , a = Nothing + , b = Nothing } ) -lineRangeStyles : Focus -> Int -> String -lineRangeStyles focus lineNumber = - case ( focus.a, focus.b ) of - ( Just lineA, Just lineB ) -> +lineRangeStyles : Maybe Int -> Int -> Focus -> String +lineRangeStyles group lineNumber focus = + case ( focus.group, focus.a, focus.b ) of + ( _, Just lineA, Just lineB ) -> let ( a, b ) = if lineA < lineB then @@ -233,14 +234,14 @@ lineRangeStyles focus lineNumber = else ( lineB, lineA ) in - if lineNumber >= a && lineNumber <= b then + if group == focus.group && lineNumber >= a && lineNumber <= b then "-focus" else "" - ( Just lineA, Nothing ) -> - if lineA == lineNumber then + ( _, Just lineA, Nothing ) -> + if group == focus.group && lineA == lineNumber then "-focus" else @@ -248,3 +249,52 @@ lineRangeStyles focus lineNumber = _ -> "" + + +lineNumberChanged : Maybe Focus -> Focus -> Maybe Int +lineNumberChanged maybeBefore after = + case maybeBefore of + Just before -> + case ( ( before.a, before.b ), ( after.a, after.b ) ) of + ( ( Just a1, Just b1 ), ( Just a2, Just b2 ) ) -> + if a1 /= a2 then + Just a2 + + else if b1 /= b2 then + Just b2 + + else + Nothing + + ( ( Just _, Just _ ), ( Just a2, Nothing ) ) -> + Just a2 + + ( ( Just _, Just _ ), ( Nothing, Nothing ) ) -> + Nothing + + ( ( Just a1, Nothing ), ( Just a2, Just b2 ) ) -> + if a1 /= a2 then + Just a2 + + else + Just b2 + + ( ( Nothing, Nothing ), ( Just a2, Just _ ) ) -> + Just a2 + + ( ( Nothing, Nothing ), ( Just a2, Nothing ) ) -> + Just a2 + + ( ( Just _, Nothing ), ( Just a2, Nothing ) ) -> + Just a2 + + _ -> + Nothing + + _ -> + case ( after.a, after.b ) of + ( Just a, _ ) -> + Just a + + _ -> + Nothing diff --git a/src/elm/Vela.elm b/src/elm/Vela.elm index faf831b45..977ea447c 100644 --- a/src/elm/Vela.elm +++ b/src/elm/Vela.elm @@ -1188,13 +1188,12 @@ type alias Step = , runtime : String , distribution : String , image : String - , viewing : Bool } defaultStep : Step defaultStep = - Step 0 0 0 0 "" "" Pending "" 0 0 0 0 "" "" "" "" False + Step 0 0 0 0 "" "" Pending "" 0 0 0 0 "" "" "" "" decodeStep : Decoder Step @@ -1216,8 +1215,6 @@ decodeStep = |> optional "runtime" string "" |> optional "distribution" string "" |> optional "image" string "" - -- "viewing" - |> hardcoded False decodeSteps : Decoder (List Step)