diff --git a/review/suppressed/NoImportingEverything.json b/review/suppressed/NoImportingEverything.json index be55eb55..19220714 100644 --- a/review/suppressed/NoImportingEverything.json +++ b/review/suppressed/NoImportingEverything.json @@ -23,7 +23,6 @@ { "count": 4, "filePath": "src/View/Graph/Tag.elm" }, { "count": 4, "filePath": "src/View/Graph/Tool.elm" }, { "count": 4, "filePath": "src/View/Locale.elm" }, - { "count": 4, "filePath": "src/View/Notification.elm" }, { "count": 4, "filePath": "src/View/Pathfinder/Tx/AccountTx.elm" }, { "count": 4, "filePath": "src/View/Pathfinder/Tx/Utxo.elm" }, { "count": 4, "filePath": "src/View/Search.elm" }, @@ -49,6 +48,7 @@ { "count": 3, "filePath": "src/View/Graph/Table/TxsUtxoTable.elm" }, { "count": 3, "filePath": "src/View/Graph/Table/UserAddressTagsTable.elm" }, { "count": 3, "filePath": "src/View/Landingpage.elm" }, + { "count": 3, "filePath": "src/View/Notification.elm" }, { "count": 3, "filePath": "src/View/Pathfinder/Tx/Path.elm" }, { "count": 3, "filePath": "themes/Iknaio.elm" }, { "count": 2, "filePath": "src/Css/Reset.elm" }, @@ -124,7 +124,6 @@ { "count": 1, "filePath": "src/Update/Graph/Address.elm" }, { "count": 1, "filePath": "src/Update/Graph/Search.elm" }, { "count": 1, "filePath": "src/Update/Graph/Table.elm" }, - { "count": 1, "filePath": "src/Update/Notification.elm" }, { "count": 1, "filePath": "src/Update/Search.elm" } ] } diff --git a/src/Update/Pathfinder/Node.elm b/src/Update/Pathfinder/Node.elm index 7ce5b373..1e711883 100644 --- a/src/Update/Pathfinder/Node.elm +++ b/src/Update/Pathfinder/Node.elm @@ -1,7 +1,8 @@ module Update.Pathfinder.Node exposing (Node, move, release) -import Animation exposing (Animation) +import Animation exposing (Animation, Clock) import Model.Graph.Coords exposing (Coords) +import Model.Pathfinder.Id exposing (Id) type alias Node a = @@ -10,6 +11,9 @@ type alias Node a = , dy : Float , x : Float , y : Animation + , clock : Clock + , opacity : Animation + , id : Id } diff --git a/src/Util/Annotations.elm b/src/Util/Annotations.elm index b55ce74a..145d635d 100644 --- a/src/Util/Annotations.elm +++ b/src/Util/Annotations.elm @@ -1,9 +1,20 @@ -module Util.Annotations exposing (AnnotationItem, AnnotationModel, empty, getAnnotation, set, setColor, setLabel, toList) +module Util.Annotations exposing (AnnotationItem, AnnotationModel, annotationToAttrAndLabel, empty, getAnnotation, set, setColor, setLabel, toList) +import Animation as A import Color exposing (Color) +import Css import Dict exposing (Dict) +import Html.Styled import Model.Pathfinder.Id exposing (Id) +import Msg.Pathfinder exposing (Msg(..)) import RecordSetter as Rs +import Svg.Styled as Svg exposing (Svg, text) +import Svg.Styled.Attributes as Svg exposing (css, opacity, transform) +import Theme.Html.GraphComponents as HtmlGraphComponents +import Theme.Svg.GraphComponents as GraphComponents +import Update.Pathfinder.Node exposing (Node) +import Util.Graph exposing (translate) +import Util.View exposing (onClickWithStop) type AnnotationModel @@ -54,3 +65,70 @@ setColor item clr (Annotation m) = getAnnotation : Id -> AnnotationModel -> Maybe AnnotationItem getAnnotation item (Annotation m) = Dict.get item m.annotations + + +annotationToAttrAndLabel : Node a -> { b | height : Float, width : Float } -> Float -> AnnotationItem -> ( List (Svg.Attribute Msg), List (Svg Msg) ) +annotationToAttrAndLabel node details offset ann = + let + colorAttributes prop = + case ann.color of + Just c -> + Color.toCssString c + |> Css.property prop + |> Css.important + |> List.singleton + |> css + |> List.singleton + + _ -> + [] + in + ( colorAttributes "fill" + , (if String.length ann.label > 0 then + HtmlGraphComponents.annotationLabelWithAttributes + (HtmlGraphComponents.annotationLabelAttributes + |> Rs.s_annotationLabel + (css + [ Css.display Css.inlineBlock + ] + :: colorAttributes "border-color" + ) + ) + { annotationLabel = { labelText = ann.label } } + |> List.singleton + |> Html.Styled.div + [ css + [ Css.pct 100 |> Css.width + , Css.textAlign Css.center + ] + ] + |> List.singleton + |> Svg.foreignObject + [ translate + 0 + (details.height + + offset + ) + |> transform + , details.width + |> String.fromFloat + |> Svg.width + , (GraphComponents.annotationLabel_details.height + + GraphComponents.annotationLabel_details.strokeWidth + * 2 + ) + * (1 + (toFloat <| String.length ann.label // 13)) + |> String.fromFloat + |> Svg.height + , A.animate node.clock node.opacity + |> String.fromFloat + |> opacity + , UserOpensAddressAnnotationDialog node.id |> onClickWithStop + , css [ Css.cursor Css.pointer ] + ] + + else + text "" + ) + |> List.singleton + ) diff --git a/src/View/Pathfinder.elm b/src/View/Pathfinder.elm index 954517ed..3cc7f00a 100644 --- a/src/View/Pathfinder.elm +++ b/src/View/Pathfinder.elm @@ -235,6 +235,9 @@ topCenterPanel plugins ms vc gc model = Pathfinder.SelectedAddress _ -> False + Pathfinder.SelectedTx _ -> + False + _ -> True , pointerTool = model.pointerTool @@ -263,6 +266,9 @@ toolbarHovercardView vc m ( hcid, hc ) = ( Annotation, Pathfinder.SelectedAddress id ) -> annotationHovercardView vc id (Annotations.getAnnotation id m.annotations) hc + ( Annotation, Pathfinder.SelectedTx id ) -> + annotationHovercardView vc id (Annotations.getAnnotation id m.annotations) hc + _ -> none @@ -1369,7 +1375,7 @@ graphSvg plugins _ vc gc model bbox = , gradient "account" { outgoing = False, reverse = True } ] , Svg.lazy7 Network.addresses plugins vc gc model.colors model.clusters model.annotations model.network.addresses - , Svg.lazy4 Network.txs plugins vc gc model.network.txs + , Svg.lazy5 Network.txs plugins vc gc model.annotations model.network.txs , Svg.lazy5 Network.edges plugins vc gc model.network.addresses model.network.txs , drawDragSelector vc model diff --git a/src/View/Pathfinder/Address.elm b/src/View/Pathfinder/Address.elm index 0d8a330c..bc9a83ae 100644 --- a/src/View/Pathfinder/Address.elm +++ b/src/View/Pathfinder/Address.elm @@ -6,7 +6,6 @@ import Color import Config.Pathfinder as Pathfinder import Config.View as View import Css -import Html.Styled import Html.Styled.Attributes as Html import Html.Styled.Events exposing (onMouseLeave) import Init.Pathfinder.Id as Id @@ -23,13 +22,12 @@ import Msg.Pathfinder exposing (Msg(..)) import Plugin.View exposing (Plugins) import RecordSetter as Rs import RemoteData -import Svg.Styled exposing (Svg, g, image, text) +import Svg.Styled as Svg exposing (Svg, g, image, text) import Svg.Styled.Attributes as Svg exposing (css, opacity, transform) import Svg.Styled.Events exposing (onMouseOver, preventDefaultOn, stopPropagationOn) -import Theme.Html.GraphComponents as HtmlGraphComponents import Theme.Svg.GraphComponents as GraphComponents import Theme.Svg.Icons as Icons -import Util.Annotations as Annotations +import Util.Annotations as Annotations exposing (annotationToAttrAndLabel) import Util.Data exposing (isAccountLike) import Util.Graph exposing (decodeCoords, translate) import Util.View exposing (onClickWithStop, truncateLongIdentifierWithLengths) @@ -113,81 +111,23 @@ view _ vc _ colors address getCluster annotation = Nothing ) + offset = + 2 + + if nodeLabel == Nothing then + -GraphComponents.addressNodeExchangeLabel_details.height + + else + 0 + ( annAttr, label ) = - case annotation of - Just ann -> - let - colorAttributes prop = - case ann.color of - Just c -> - Color.toCssString c - |> Css.property prop - |> Css.important - |> List.singleton - |> css - |> List.singleton - - _ -> - [] - in - ( colorAttributes "fill" - , (if String.length ann.label > 0 then - HtmlGraphComponents.annotationLabelWithAttributes - (HtmlGraphComponents.annotationLabelAttributes - |> Rs.s_annotationLabel - (css - [ Css.display Css.inlineBlock - ] - :: colorAttributes "border-color" - ) - ) - { annotationLabel = { labelText = ann.label } } - |> List.singleton - |> Html.Styled.div - [ css - [ Css.pct 100 |> Css.width - , Css.textAlign Css.center - ] - ] - |> List.singleton - |> Svg.Styled.foreignObject - [ translate - 0 - (GraphComponents.addressNode_details.height - + (if nodeLabel == Nothing then - -GraphComponents.addressNodeExchangeLabel_details.height - - else - 0 - ) - + 2 - ) - |> transform - , GraphComponents.addressNode_details.width - |> String.fromFloat - |> Svg.width - , (GraphComponents.annotationLabel_details.height - + GraphComponents.annotationLabel_details.strokeWidth - * 2 - ) - * (1 + (toFloat <| String.length ann.label // 13)) - |> String.fromFloat - |> Svg.height - , A.animate address.clock address.opacity - |> String.fromFloat - |> opacity - , UserOpensAddressAnnotationDialog address.id |> onClickWithStop - , css [ Css.cursor Css.pointer ] - ] - - else - text "" - ) - |> List.singleton + annotation + |> Maybe.map + (annotationToAttrAndLabel + address + GraphComponents.addressNode_details + offset ) - - _ -> - ( [], [] ) + |> Maybe.withDefault ( [], [] ) in g [ translate @@ -337,7 +277,7 @@ toNodeIconHtml address cluster = Icons.iconsUntagged {} ) |> List.singleton - |> Svg.Styled.svg + |> Svg.svg [ Svg.width "24" , Svg.height "24" ] diff --git a/src/View/Pathfinder/Network.elm b/src/View/Pathfinder/Network.elm index 9774d25a..b1afe2bb 100644 --- a/src/View/Pathfinder/Network.elm +++ b/src/View/Pathfinder/Network.elm @@ -25,7 +25,8 @@ addresses plugins vc gc colors clusters annotations = Dict.foldl (\id address svg -> ( Id.toString id - , Svg.lazy7 Address.view plugins vc gc colors address (flip Dict.get clusters) (Annotations.getAnnotation id annotations) + , (Annotations.getAnnotation id annotations) + |> Svg.lazy7 Address.view plugins vc gc colors address (flip Dict.get clusters) ) :: svg ) @@ -33,12 +34,13 @@ addresses plugins vc gc colors clusters annotations = >> Keyed.node "g" [] -txs : Plugins -> View.Config -> Pathfinder.Config -> Dict Id Tx -> Svg Msg -txs plugins vc gc = +txs : Plugins -> View.Config -> Pathfinder.Config -> Annotations.AnnotationModel -> Dict Id Tx -> Svg Msg +txs plugins vc gc annotations = Dict.foldl (\id tx svg -> ( Id.toString id - , Svg.lazy4 Tx.view plugins vc gc tx + , (Annotations.getAnnotation id annotations) + |> Svg.lazy5 Tx.view plugins vc gc tx ) :: svg ) diff --git a/src/View/Pathfinder/Tx.elm b/src/View/Pathfinder/Tx.elm index 22fe8d9b..f2e2e36a 100644 --- a/src/View/Pathfinder/Tx.elm +++ b/src/View/Pathfinder/Tx.elm @@ -10,18 +10,21 @@ import Msg.Pathfinder exposing (Msg) import Plugin.View exposing (Plugins) import Svg.Styled exposing (Svg) import Svg.Styled.Lazy as Svg +import Util.Annotations as Annotations import View.Pathfinder.Tx.AccountTx as AccountTx import View.Pathfinder.Tx.Utxo as Utxo -view : Plugins -> View.Config -> Pathfinder.Config -> Tx -> Svg Msg -view plugins vc gc tx = +view : Plugins -> View.Config -> Pathfinder.Config -> Tx -> Maybe Annotations.AnnotationItem -> Svg Msg +view plugins vc gc tx annotation = case tx.type_ of Utxo t -> - Utxo.view plugins vc gc tx t + annotation + |> Utxo.view plugins vc gc tx t Account t -> - AccountTx.view plugins vc gc tx t + annotation + |> AccountTx.view plugins vc gc tx t edge : Plugins -> View.Config -> Pathfinder.Config -> Dict Id Address -> Tx -> ( String, Svg Msg ) diff --git a/src/View/Pathfinder/Tx/AccountTx.elm b/src/View/Pathfinder/Tx/AccountTx.elm index f921b8f7..da7818df 100644 --- a/src/View/Pathfinder/Tx/AccountTx.elm +++ b/src/View/Pathfinder/Tx/AccountTx.elm @@ -24,6 +24,7 @@ import Svg.Styled.Keyed as Keyed import Svg.Styled.Lazy as Svg import Theme.Svg.GraphComponents as GraphComponents exposing (txNodeEthAttributes) import Theme.Svg.Icons as Icons +import Util.Annotations as Annotations exposing (annotationToAttrAndLabel) import Util.Graph exposing (decodeCoords, translate) import Util.Pathfinder exposing (getAddress) import Util.View exposing (onClickWithStop) @@ -32,8 +33,8 @@ import View.Pathfinder.Tx.Path exposing (inPath, inPathHovered, outPath, outPath import View.Pathfinder.Tx.Utils exposing (AnimatedPosTrait, signX, toPosition) -view : Plugins -> View.Config -> Pathfinder.Config -> Tx -> AccountTx -> Svg Msg -view _ vc _ tx accTx = +view : Plugins -> View.Config -> Pathfinder.Config -> Tx -> AccountTx -> Maybe Annotations.AnnotationItem -> Svg Msg +view _ vc _ tx accTx annotation = let fd = GraphComponents.txNodeEthNodeEllipse_details @@ -43,53 +44,77 @@ view _ vc _ tx accTx = adjY = fd.y + fd.height / 2 + + offset = + 2 + + (if vc.showTimestampOnTxEdge then + 0 + + else + -GraphComponents.txNodeEthTimestamp_details.height + ) + + ( annAttr, label ) = + annotation + |> Maybe.map + (annotationToAttrAndLabel + tx + GraphComponents.txNodeEth_details + offset + ) + |> Maybe.withDefault ( [], [] ) in - GraphComponents.txNodeEthWithAttributes - { txNodeEthAttributes - | txNodeEth = - [ translate - ((tx.x + tx.dx) * unit - adjX) - ((A.animate tx.clock tx.y + tx.dy) * unit - adjY) - |> transform - , A.animate tx.clock tx.opacity - |> String.fromFloat - |> opacity - , UserClickedTx tx.id |> onClickWithStop - , UserPushesLeftMouseButtonOnUtxoTx tx.id - |> Util.Graph.mousedown - , UserMovesMouseOverUtxoTx tx.id - |> onMouseOver - , UserMovesMouseOutUtxoTx tx.id - |> onMouseLeave - , css [ Css.cursor Css.pointer ] - , Id.toString tx.id - |> Svg.Styled.Attributes.id - , decodeCoords Coords.Coords - |> Json.Decode.map (\c -> ( UserOpensContextMenu c (ContextMenu.TransactionContextMenu tx.id), True )) - |> preventDefaultOn "contextmenu" - ] - } - { txNodeEth = - { highlightVisible = tx.selected - , date = Locale.timestampDateUniform vc.locale accTx.raw.timestamp - , time = Locale.timestampTimeUniform vc.locale vc.showTimeZoneOffset accTx.raw.timestamp - , inputValue = Locale.currency vc.locale [ ( asset accTx.raw.network accTx.raw.currency, accTx.value ) ] - , timestampVisible = vc.showTimestampOnTxEdge - , startingPointVisible = tx.isStartingPoint || tx.selected + g + [ translate + ((tx.x + tx.dx) * unit - adjX) + ((A.animate tx.clock tx.y + tx.dy) * unit - adjY) + |> transform + , A.animate tx.clock tx.opacity + |> String.fromFloat + |> opacity + ] + (GraphComponents.txNodeEthWithAttributes + { txNodeEthAttributes + | txNodeEth = + [ UserClickedTx tx.id |> onClickWithStop + , UserPushesLeftMouseButtonOnUtxoTx tx.id + |> Util.Graph.mousedown + , UserMovesMouseOverUtxoTx tx.id + |> onMouseOver + , UserMovesMouseOutUtxoTx tx.id + |> onMouseLeave + , css [ Css.cursor Css.pointer ] + , Id.toString tx.id + |> Svg.Styled.Attributes.id + , decodeCoords Coords.Coords + |> Json.Decode.map (\c -> ( UserOpensContextMenu c (ContextMenu.TransactionContextMenu tx.id), True )) + |> preventDefaultOn "contextmenu" + ] + , nodeEllipse = annAttr } - , iconsNodeMarker = - { variant = - case ( tx.selected, tx.isStartingPoint ) of - ( True, _ ) -> - Icons.iconsNodeMarkerPurposeSelectedNode {} - - ( False, False ) -> - text "" - - ( False, True ) -> - Icons.iconsNodeMarkerPurposeStartingPoint {} + { txNodeEth = + { highlightVisible = tx.selected + , date = Locale.timestampDateUniform vc.locale accTx.raw.timestamp + , time = Locale.timestampTimeUniform vc.locale vc.showTimeZoneOffset accTx.raw.timestamp + , inputValue = Locale.currency vc.locale [ ( asset accTx.raw.network accTx.raw.currency, accTx.value ) ] + , timestampVisible = vc.showTimestampOnTxEdge + , startingPointVisible = tx.isStartingPoint || tx.selected + } + , iconsNodeMarker = + { variant = + case ( tx.selected, tx.isStartingPoint ) of + ( True, _ ) -> + Icons.iconsNodeMarkerPurposeSelectedNode {} + + ( False, False ) -> + text "" + + ( False, True ) -> + Icons.iconsNodeMarkerPurposeStartingPoint {} + } } - } + :: label + ) edge : Plugins -> View.Config -> Pathfinder.Config -> Bool -> Dict Id Address -> AccountTx -> AnimatedPosTrait x -> Svg Msg diff --git a/src/View/Pathfinder/Tx/Utxo.elm b/src/View/Pathfinder/Tx/Utxo.elm index 98d7495e..999de150 100644 --- a/src/View/Pathfinder/Tx/Utxo.elm +++ b/src/View/Pathfinder/Tx/Utxo.elm @@ -23,6 +23,7 @@ import Svg.Styled.Lazy as Svg import Theme.Svg.GraphComponents as GraphComponents exposing (txNodeUtxoAttributes) import Theme.Svg.Icons as Icons import Tuple exposing (pair, second) +import Util.Annotations as Annotations exposing (annotationToAttrAndLabel) import Util.Graph exposing (decodeCoords, translate) import Util.View exposing (onClickWithStop) import View.Locale as Locale @@ -30,8 +31,8 @@ import View.Pathfinder.Tx.Path exposing (inPath, inPathHovered, outPath, outPath import View.Pathfinder.Tx.Utils exposing (AnimatedPosTrait, signX, toPosition) -view : Plugins -> View.Config -> Pathfinder.Config -> Tx -> UtxoTx -> Svg Msg -view _ vc _ tx utxo = +view : Plugins -> View.Config -> Pathfinder.Config -> Tx -> UtxoTx -> Maybe Annotations.AnnotationItem -> Svg Msg +view _ vc _ tx utxo annotation = let id = tx.id @@ -48,53 +49,77 @@ view _ vc _ tx utxo = adjY = fd.y + fd.height / 2 + + offset = + 2 + + (if vc.showTimestampOnTxEdge then + 0 + + else + -GraphComponents.txNodeUtxoTxText_details.height + ) + + ( annAttr, label ) = + annotation + |> Maybe.map + (annotationToAttrAndLabel + tx + GraphComponents.txNodeUtxo_details + offset + ) + |> Maybe.withDefault ( [], [] ) in - GraphComponents.txNodeUtxoWithAttributes - { txNodeUtxoAttributes - | txNodeUtxo = - [ translate - ((tx.x + tx.dx) * unit - adjX) - ((A.animate tx.clock tx.y + tx.dy) * unit - adjY) - |> transform - , A.animate tx.clock tx.opacity - |> String.fromFloat - |> opacity - , UserClickedTx id |> onClickWithStop - , UserPushesLeftMouseButtonOnUtxoTx id - |> Util.Graph.mousedown - , UserMovesMouseOverUtxoTx id - |> onMouseOver - , UserMovesMouseOutUtxoTx id - |> onMouseLeave - , css [ Css.cursor Css.pointer ] - , Id.toString id - |> Svg.Styled.Attributes.id - , decodeCoords Coords.Coords - |> Json.Decode.map (\c -> ( UserOpensContextMenu c (ContextMenu.TransactionContextMenu id), True )) - |> preventDefaultOn "contextmenu" - ] - } - { txNodeUtxo = - { hasMultipleInOutputs = anyIsNotVisible utxo.inputs || anyIsNotVisible utxo.outputs - , highlightVisible = tx.selected || tx.hovered - , date = Locale.timestampDateUniform vc.locale utxo.raw.timestamp - , time = Locale.timestampTimeUniform vc.locale vc.showTimeZoneOffset utxo.raw.timestamp - , timestampVisible = vc.showTimestampOnTxEdge - , startingPointVisible = tx.isStartingPoint || tx.selected + g + [ translate + ((tx.x + tx.dx) * unit - adjX) + ((A.animate tx.clock tx.y + tx.dy) * unit - adjY) + |> transform + , A.animate tx.clock tx.opacity + |> String.fromFloat + |> opacity + ] + (GraphComponents.txNodeUtxoWithAttributes + { txNodeUtxoAttributes + | txNodeUtxo = + [ UserClickedTx id |> onClickWithStop + , UserPushesLeftMouseButtonOnUtxoTx id + |> Util.Graph.mousedown + , UserMovesMouseOverUtxoTx id + |> onMouseOver + , UserMovesMouseOutUtxoTx id + |> onMouseLeave + , css [ Css.cursor Css.pointer ] + , Id.toString id + |> Svg.Styled.Attributes.id + , decodeCoords Coords.Coords + |> Json.Decode.map (\c -> ( UserOpensContextMenu c (ContextMenu.TransactionContextMenu id), True )) + |> preventDefaultOn "contextmenu" + ] + , txNode = annAttr } - , iconsNodeMarker = - { variant = - case ( tx.selected, tx.isStartingPoint ) of - ( True, _ ) -> - Icons.iconsNodeMarkerPurposeSelectedNode {} - - ( False, False ) -> - text "" - - ( False, True ) -> - Icons.iconsNodeMarkerPurposeStartingPoint {} + { txNodeUtxo = + { hasMultipleInOutputs = anyIsNotVisible utxo.inputs || anyIsNotVisible utxo.outputs + , highlightVisible = tx.selected || tx.hovered + , date = Locale.timestampDateUniform vc.locale utxo.raw.timestamp + , time = Locale.timestampTimeUniform vc.locale vc.showTimeZoneOffset utxo.raw.timestamp + , timestampVisible = vc.showTimestampOnTxEdge + , startingPointVisible = tx.isStartingPoint || tx.selected + } + , iconsNodeMarker = + { variant = + case ( tx.selected, tx.isStartingPoint ) of + ( True, _ ) -> + Icons.iconsNodeMarkerPurposeSelectedNode {} + + ( False, False ) -> + text "" + + ( False, True ) -> + Icons.iconsNodeMarkerPurposeStartingPoint {} + } } - } + :: label + ) edge : Plugins -> View.Config -> Pathfinder.Config -> Bool -> UtxoTx -> AnimatedPosTrait x -> Svg Msg