Commit bb705423 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

[Graph] refresh graph properly when clicking through the graphs in tree

This is achieved by introducing graph Stage. We can split initial
rendering then from hooks when ready (like showing marked selections)
and the cleanup stuff.
parent f77ad1c8
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
} }
#graph-explorer #sp-container { #graph-explorer #sp-container {
position: absolute; position: absolute;
left: 63%; left: 70%;
top: 150px; top: 150px;
z-index: 1; z-index: 1;
border: 1px white solid; border: 1px white solid;
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#sp-container #sp-container
position: absolute position: absolute
left: 63% left: 70%
top: 150px top: 150px
z-index: 1 z-index: 1
border: 1px white solid border: 1px white solid
......
...@@ -4,7 +4,7 @@ module Gargantext.Components.Graph ...@@ -4,7 +4,7 @@ module Gargantext.Components.Graph
-- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings -- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings
-- ) -- )
where where
import Prelude (bind, discard, pure, ($), unit, map) import Prelude (bind, const, discard, pure, ($), unit, map, (==), (&&), class Eq)
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Map as Map import Data.Map as Map
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
...@@ -15,6 +15,7 @@ import Data.Tuple.Nested ((/\)) ...@@ -15,6 +15,7 @@ import Data.Tuple.Nested ((/\))
import DOM.Simple (createElement, setAttr) import DOM.Simple (createElement, setAttr)
import DOM.Simple.Console (log, log2) import DOM.Simple.Console (log, log2)
import DOM.Simple.Types (Element) import DOM.Simple.Types (Element)
import Effect.Class (liftEffect)
import Effect.Timer (setTimeout) import Effect.Timer (setTimeout)
import FFI.Simple (delay, (..)) import FFI.Simple (delay, (..))
import Reactix as R import Reactix as R
...@@ -29,6 +30,8 @@ type OnProps = () ...@@ -29,6 +30,8 @@ type OnProps = ()
type Graph = SigmaxTypes.Graph SigmaxTypes.Node SigmaxTypes.Edge type Graph = SigmaxTypes.Graph SigmaxTypes.Node SigmaxTypes.Edge
data Stage = Init | Ready | Cleanup
type Props sigma forceatlas2 = type Props sigma forceatlas2 =
( elRef :: R.Ref (Nullable Element) ( elRef :: R.Ref (Nullable Element)
, forceAtlas2Settings :: forceatlas2 , forceAtlas2Settings :: forceatlas2
...@@ -36,6 +39,7 @@ type Props sigma forceatlas2 = ...@@ -36,6 +39,7 @@ type Props sigma forceatlas2 =
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds , selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, sigmaSettings :: sigma , sigmaSettings :: sigma
, sigmaRef :: R.Ref Sigmax.Sigma , sigmaRef :: R.Ref Sigmax.Sigma
, stage :: R.State Stage
) )
graph :: forall s fa2. Record (Props s fa2) -> R.Element graph :: forall s fa2. Record (Props s fa2) -> R.Element
...@@ -45,14 +49,18 @@ graphCpt :: forall s fa2. R.Component (Props s fa2) ...@@ -45,14 +49,18 @@ graphCpt :: forall s fa2. R.Component (Props s fa2)
graphCpt = R.hooksComponent "Graph" cpt graphCpt = R.hooksComponent "Graph" cpt
where where
cpt props _ = do cpt props _ = do
let nodesMap = SigmaxTypes.nodesMap props.graph stageHooks props
let selectedNodeIds = props.selectedNodeIds
R.useEffect' $ do -- NOTE: This div is not empty after sigma initializes.
Sigmax.dependOnSigma (R.readRef props.sigmaRef) "[graphCpt] no sigma" $ \sigma -> -- When we change state, we make it empty though.
Sigmax.markSelectedNodes sigma (fst selectedNodeIds) nodesMap --pure $ RH.div { ref: props.elRef, style: {height: "95%"} } []
pure $ case R.readNullableRef props.elRef of
Nothing -> RH.div {} []
Just el -> R.createPortal [] el
stageHooks props@{stage: (Init /\ setStage)} = do
R.useEffectOnce $ do R.useEffectOnce $ do
log "[graphCpt] effect once"
let rSigma = R.readRef props.sigmaRef let rSigma = R.readRef props.sigmaRef
case Sigmax.readSigma rSigma of case Sigmax.readSigma rSigma of
...@@ -76,20 +84,26 @@ graphCpt = R.hooksComponent "Graph" cpt ...@@ -76,20 +84,26 @@ graphCpt = R.hooksComponent "Graph" cpt
Sigma.startForceAtlas2 sig props.forceAtlas2Settings Sigma.startForceAtlas2 sig props.forceAtlas2Settings
-- bind the click event only initially, when ref was empty -- bind the click event only initially, when ref was empty
Sigmax.bindSelectedNodesClick props.sigmaRef selectedNodeIds Sigmax.bindSelectedNodesClick props.sigmaRef props.selectedNodeIds
Just sig -> do Just sig -> do
pure unit pure unit
setStage $ const $ Ready
delay unit $ \_ -> do delay unit $ \_ -> do
log "[GraphCpt] cleanup" log "[graphCpt] cleanup"
pure $ pure unit pure $ pure unit
-- NOTE: This div is not empty after sigma initializes. stageHooks props@{stage: (Ready /\ setStage)} = do
-- When we change state, we make it empty though. let nodesMap = SigmaxTypes.nodesMap props.graph
--pure $ RH.div { ref: props.elRef, style: {height: "95%"} } []
pure $ case R.readNullableRef props.elRef of -- TODO Probably this can be optimized to re-mark selected nodes only when they changed
Nothing -> RH.div {} [] R.useEffect' $ do
Just el -> R.createPortal [] el Sigmax.dependOnSigma (R.readRef props.sigmaRef) "[graphCpt] no sigma" $ \sigma ->
Sigmax.markSelectedNodes sigma (fst props.selectedNodeIds) nodesMap
stageHooks _ = pure unit
type SigmaSettings = type SigmaSettings =
( animationsTime :: Number ( animationsTime :: Number
......
...@@ -12,7 +12,7 @@ import Data.Sequence as Seq ...@@ -12,7 +12,7 @@ import Data.Sequence as Seq
import Data.Set as Set import Data.Set as Set
import Data.Tuple (fst, snd, Tuple(..)) import Data.Tuple (fst, snd, Tuple(..))
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import DOM.Simple.Console (log2) import DOM.Simple.Console (log, log2)
import DOM.Simple.Types (Element) import DOM.Simple.Types (Element)
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Reactix as R import Reactix as R
...@@ -20,6 +20,7 @@ import Reactix.DOM.HTML as RH ...@@ -20,6 +20,7 @@ import Reactix.DOM.HTML as RH
import Gargantext.Hooks.Loader (useLoader) import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Hooks.Sigmax (Sigma) import Gargantext.Hooks.Sigmax (Sigma)
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Components.GraphExplorer.Controls as Controls import Gargantext.Components.GraphExplorer.Controls as Controls
import Gargantext.Components.GraphExplorer.Sidebar as Sidebar import Gargantext.Components.GraphExplorer.Sidebar as Sidebar
...@@ -71,12 +72,30 @@ explorerCpt :: R.Component Props ...@@ -71,12 +72,30 @@ explorerCpt :: R.Component Props
explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
where where
cpt {frontends, graph, graphId, mCurrentRoute, mMetaData, session, sessions, treeId} _ = do cpt {frontends, graph, graphId, mCurrentRoute, mMetaData, session, sessions, treeId} _ = do
R.useEffect' $ do
case graph of
Nothing -> log "[explorerCpt] empty graph data"
Just (SigmaxTypes.Graph {nodes}) -> do
log2 "[explorerCpt] number of nodes" $ Seq.length nodes
dataRef <- R.useRef graph
graphRef <- R.useRef null graphRef <- R.useRef null
controls <- Controls.useGraphControls controls <- Controls.useGraphControls
state <- useExplorerState
showLogin <- snd <$> R.useState' true showLogin <- snd <$> R.useState' true
selectedNodeIds <- R.useState' $ Set.empty selectedNodeIds <- R.useState' $ Set.empty
R.useEffect' $ do
case Tuple (R.readRef dataRef) graph of
Tuple Nothing Nothing -> pure unit
Tuple (Just g1) (Just g2) | SigmaxTypes.eqGraph g1 g2 -> pure unit
_ -> do
log "[explorerCpt] data changed"
let rSigma = R.readRef controls.sigmaRef
Sigmax.cleanupSigma rSigma "explorerCpt"
R.setRef dataRef graph
snd selectedNodeIds $ const Set.empty
snd controls.graphStage $ const Graph.Init
R.useEffect' $ do R.useEffect' $ do
if fst controls.showSidePanel == GET.InitialClosed && (not Set.isEmpty $ fst selectedNodeIds) then if fst controls.showSidePanel == GET.InitialClosed && (not Set.isEmpty $ fst selectedNodeIds) then
snd controls.showSidePanel $ \_ -> GET.Opened snd controls.showSidePanel $ \_ -> GET.Opened
...@@ -96,8 +115,8 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt ...@@ -96,8 +115,8 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
] ]
, row [ Controls.controls controls ] , row [ Controls.controls controls ]
, row [ tree {mCurrentRoute, treeId} controls showLogin , row [ tree {mCurrentRoute, treeId} controls showLogin
, RH.div { ref: graphRef, id: "graph-view", className: "col-md-12", style: {height: "95%"} } [] -- graph container , RH.div { ref: graphRef, id: "graph-view", className: graphClassName controls, style: {height: "95%"} } [] -- graph container
, mGraph graphRef controls.sigmaRef {graphId, graph, selectedNodeIds} , mGraph graphRef controls.sigmaRef {graphId, graph, graphStage: controls.graphStage, selectedNodeIds}
, mSidebar graph mMetaData {frontends, session, selectedNodeIds, showSidePanel: fst controls.showSidePanel} , mSidebar graph mMetaData {frontends, session, selectedNodeIds, showSidePanel: fst controls.showSidePanel}
] ]
, row [ , row [
...@@ -112,6 +131,11 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt ...@@ -112,6 +131,11 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
tree {mCurrentRoute: route, treeId: root} _ showLogin = tree {mCurrentRoute: route, treeId: root} _ showLogin =
RH.div {className: "col-md-2", style: {paddingTop: "60px"}} RH.div {className: "col-md-2", style: {paddingTop: "60px"}}
[forest {sessions, route, frontends, showLogin}] [forest {sessions, route, frontends, showLogin}]
graphClassName :: Record Controls.Controls -> String
graphClassName {showSidePanel: (GET.Opened /\ _), showTree: (true /\ _)} = "col-md-8"
graphClassName {showTree: (true /\ _)} = "col-md-10"
graphClassName {showSidePanel: (GET.Opened /\ _)} = "col-md-10"
graphClassName _ = "col-md-12"
outer = RH.div { className: "col-md-12" } outer = RH.div { className: "col-md-12" }
inner = RH.div { className: "container-fluid", style: { paddingTop: "90px" } } inner = RH.div { className: "container-fluid", style: { paddingTop: "90px" } }
...@@ -125,10 +149,11 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt ...@@ -125,10 +149,11 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
-> R.Ref Sigma -> R.Ref Sigma
-> { graphId :: GraphId -> { graphId :: GraphId
, graph :: Maybe Graph.Graph , graph :: Maybe Graph.Graph
, graphStage :: R.State Graph.Stage
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds} , selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds}
-> R.Element -> R.Element
mGraph _ _ {graph: Nothing} = RH.div {} [] mGraph _ _ {graph: Nothing} = RH.div {} []
mGraph graphRef sigmaRef {graphId, graph: Just graph, selectedNodeIds} = graphView graphRef sigmaRef {graphId, graph, selectedNodeIds} mGraph graphRef sigmaRef r@{graph: Just graph} = graphView graphRef sigmaRef $ r { graph = graph }
mSidebar :: Maybe Graph.Graph mSidebar :: Maybe Graph.Graph
-> Maybe GET.MetaData -> Maybe GET.MetaData
...@@ -148,27 +173,10 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt ...@@ -148,27 +173,10 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
, showSidePanel , showSidePanel
} }
useExplorerState :: R.Hooks (Record GET.State)
useExplorerState = do pure {}
{- corpusId <- R.useState' 0
cursorSize <- R.useState' 0.0
filePath <- R.useState' ""
graphData <- R.useState' initialGraphData
legendData <- R.useState' []
multiNodeSelection <- R.useState' false
selectedNodes <- R.useState' Set.empty
showControls <- R.useState' false
showSidePanel <- R.useState' false
showTree <- R.useState' false
sigmaGraphData <- R.useState' (Nothing :: Maybe Graph.Graph)
sigmaSettings <- R.useState' Graph.sigmaSettings
treeId <- R.useState' (Nothing :: Maybe TreeId) -}
--treeId : Nothing
type GraphProps = ( type GraphProps = (
graphId :: GraphId graphId :: GraphId
, graph :: Graph.Graph , graph :: Graph.Graph
, graphStage :: R.State Graph.Stage
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds , selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
) )
...@@ -186,6 +194,7 @@ graphView elRef sigmaRef props = R.createElement el props [] ...@@ -186,6 +194,7 @@ graphView elRef sigmaRef props = R.createElement el props []
, selectedNodeIds , selectedNodeIds
, sigmaSettings: Graph.sigmaSettings , sigmaSettings: Graph.sigmaSettings
, sigmaRef: sigmaRef , sigmaRef: sigmaRef
, stage: props.graphStage
} }
convert :: GET.GraphData -> Tuple (Maybe GET.MetaData) Graph.Graph convert :: GET.GraphData -> Tuple (Maybe GET.MetaData) Graph.Graph
......
...@@ -32,6 +32,7 @@ import Gargantext.Utils.Reactix as R2 ...@@ -32,6 +32,7 @@ import Gargantext.Utils.Reactix as R2
type Controls = type Controls =
( cursorSize :: R.State Number ( cursorSize :: R.State Number
, graphStage :: R.State Graph.Stage
, multiNodeSelect :: R.Ref Boolean , multiNodeSelect :: R.Ref Boolean
, showControls :: R.State Boolean , showControls :: R.State Boolean
, showSidePanel :: R.State GET.SidePanelState , showSidePanel :: R.State GET.SidePanelState
...@@ -76,7 +77,6 @@ controlsCpt = R.hooksComponent "GraphControls" cpt ...@@ -76,7 +77,6 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
localControls <- initialLocalControls localControls <- initialLocalControls
-- ref to track automatic FA pausing -- ref to track automatic FA pausing
-- If user pauses FA before auto is triggered, clear the timeoutId -- If user pauses FA before auto is triggered, clear the timeoutId
-- TODO: mFAPauseRef needs to be set higher up the tree
mFAPauseRef <- R.useRef Nothing mFAPauseRef <- R.useRef Nothing
--R.useEffect $ handleForceAtlasPause props.sigmaRef localControls.pauseForceAtlas mFAPauseRef --R.useEffect $ handleForceAtlasPause props.sigmaRef localControls.pauseForceAtlas mFAPauseRef
...@@ -84,7 +84,6 @@ controlsCpt = R.hooksComponent "GraphControls" cpt ...@@ -84,7 +84,6 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
R.useEffectOnce' $ do R.useEffectOnce' $ do
timeoutId <- setTimeout 2000 $ do timeoutId <- setTimeout 2000 $ do
--R.setRef mFAPauseRef Nothing
let (toggled /\ setToggled) = localControls.pauseForceAtlas let (toggled /\ setToggled) = localControls.pauseForceAtlas
if toggled then if toggled then
setToggled $ const false setToggled $ const false
...@@ -122,6 +121,7 @@ controlsCpt = R.hooksComponent "GraphControls" cpt ...@@ -122,6 +121,7 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
useGraphControls :: R.Hooks (Record Controls) useGraphControls :: R.Hooks (Record Controls)
useGraphControls = do useGraphControls = do
cursorSize <- R.useState' 10.0 cursorSize <- R.useState' 10.0
graphStage <- R.useState' Graph.Init
multiNodeSelect <- R.useRef false multiNodeSelect <- R.useRef false
showControls <- R.useState' false showControls <- R.useState' false
showSidePanel <- R.useState' GET.InitialClosed showSidePanel <- R.useState' GET.InitialClosed
...@@ -130,6 +130,7 @@ useGraphControls = do ...@@ -130,6 +130,7 @@ useGraphControls = do
sigmaRef <- R.useRef sigma sigmaRef <- R.useRef sigma
pure { cursorSize pure { cursorSize
, graphStage
, multiNodeSelect , multiNodeSelect
, showControls , showControls
, showSidePanel , showSidePanel
......
module Gargantext.Hooks.Sigmax.Types where module Gargantext.Hooks.Sigmax.Types where
import Prelude (map, ($)) import Prelude (map, ($), (&&), (==))
import Data.Map as Map import Data.Map as Map
import Data.Sequence (Seq) import Data.Sequence (Seq)
import Data.Set as Set import Data.Set as Set
...@@ -35,3 +35,6 @@ nodesMap :: Graph Node Edge -> NodesMap ...@@ -35,3 +35,6 @@ nodesMap :: Graph Node Edge -> NodesMap
nodesMap graph = do nodesMap graph = do
let (Graph {nodes}) = graph let (Graph {nodes}) = graph
Map.fromFoldable $ map (\n -> Tuple n.id n) nodes Map.fromFoldable $ map (\n -> Tuple n.id n) nodes
eqGraph :: (Graph Node Edge) -> (Graph Node Edge) -> Boolean
eqGraph (Graph {nodes: n1, edges: e1}) (Graph {nodes: n2, edges: e2}) = (n1 == n2) && (e1 == e2)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment