Controls.purs 9.63 KB
Newer Older
James Laver's avatar
James Laver committed
1
module Gargantext.Components.GraphExplorer.Controls
2 3 4 5
 ( Controls
 , useGraphControls
 , controls
 , controlsCpt
James Laver's avatar
James Laver committed
6 7 8 9
 , getShowTree, setShowTree
 , getShowControls, setShowControls
 ) where

10
import Data.Array as A
11
import Data.Int as I
12 13
import Data.Maybe (Maybe(..), maybe)
import Data.Sequence as Seq
14 15
import Data.Set as Set
import Data.Tuple (fst, snd)
16
import Data.Tuple.Nested ((/\))
17
import Effect (Effect)
18
import Effect.Timer (setTimeout)
19
import Prelude
James Laver's avatar
James Laver committed
20 21 22
import Reactix as R
import Reactix.DOM.HTML as RH

23
import Gargantext.Components.Graph as Graph
24
import Gargantext.Components.GraphExplorer.Button (centerButton, cameraButton)
25
import Gargantext.Components.GraphExplorer.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl)
26
import Gargantext.Components.GraphExplorer.Search (nodeSearchControl)
27
import Gargantext.Components.GraphExplorer.SlideButton (labelSizeButton, mouseSelectorSizeButton)
28
import Gargantext.Components.GraphExplorer.ToggleButton (multiSelectEnabledButton, edgesToggleButton, louvainToggleButton, pauseForceAtlasButton)
29
import Gargantext.Components.GraphExplorer.Types as GET
30
import Gargantext.Hooks.Sigmax as Sigmax
31
import Gargantext.Hooks.Sigmax.Types as SigmaxT
32
import Gargantext.Sessions (Session)
33
import Gargantext.Utils.Range as Range
34 35
import Gargantext.Utils.Reactix as R2

36 37
thisModule = "Gargantext.Components.GraphExplorer.Controls"

James Laver's avatar
James Laver committed
38
type Controls =
39
  ( edgeConfluence :: R.State Range.NumberRange
40
  , edgeWeight :: R.State Range.NumberRange
41 42
  , forceAtlasState :: R.State SigmaxT.ForceAtlasState
  , graph           :: SigmaxT.SGraph
43
  , graphId         :: GET.GraphId
44
  , graphStage      :: R.State Graph.Stage
45
  , hyperdataGraph  :: GET.HyperdataGraph
46
  , multiSelectEnabled :: R.State Boolean
47
  , nodeSize        :: R.State Range.NumberRange
48 49
  , removedNodeIds  :: R.State SigmaxT.NodeIds
  , selectedNodeIds :: R.State SigmaxT.NodeIds
50
  , session         :: Session
51
  , showControls    :: R.State Boolean
52
  , showEdges       :: R.State SigmaxT.ShowEdgesState
53
  , showLouvain     :: R.State Boolean
54
  , showSidePanel   :: R.State GET.SidePanelState
55
  , showTree        :: R.State Boolean
56
  , sigmaRef        :: R.Ref Sigmax.Sigma
57
  , treeReload      :: Unit -> Effect Unit
James Laver's avatar
James Laver committed
58 59
  )

60
type LocalControls =
61
  ( labelSize :: R.State Number
62
  , mouseSelectorSize :: R.State Number
63 64 65 66
  )

initialLocalControls :: R.Hooks (Record LocalControls)
initialLocalControls = do
67
  labelSize <- R.useState' 14.0
68
  mouseSelectorSize <- R.useState' 15.0
69 70

  pure $ {
71
    labelSize
72
  , mouseSelectorSize
73 74
  }

75
controls :: Record Controls -> R.Element
James Laver's avatar
James Laver committed
76 77 78
controls props = R.createElement controlsCpt props []

controlsCpt :: R.Component Controls
79
controlsCpt = R.hooksComponentWithModule thisModule "controls" cpt
James Laver's avatar
James Laver committed
80
  where
81 82
    cpt props _ = do
      localControls <- initialLocalControls
83 84
      -- ref to track automatic FA pausing
      -- If user pauses FA before auto is triggered, clear the timeoutId
85
      mFAPauseRef <- R.useRef Nothing
86

87 88
      -- When graph is changed, cleanup the mFAPauseRef so that forceAtlas
      -- timeout is retriggered.
89 90 91 92 93
      R.useEffect' $ do
        case fst props.graphStage of
          Graph.Init -> R.setRef mFAPauseRef Nothing
          _          -> pure unit

94 95
      -- Handle case when FA is paused from outside events, eg. the automatic timer.
      R.useEffect' $ Sigmax.handleForceAtlas2Pause props.sigmaRef props.forceAtlasState mFAPauseRef
96

97 98
      -- Handle automatic edge hiding when FA is running (to prevent flickering).
      R.useEffect2' props.sigmaRef props.forceAtlasState $
99
        snd props.showEdges $ SigmaxT.forceAtlasEdgeState (fst props.forceAtlasState)
100 101

      -- Automatic opening of sidebar when a node is selected (but only first time).
102 103
      R.useEffect' $ do
        if fst props.showSidePanel == GET.InitialClosed && (not Set.isEmpty $ fst props.selectedNodeIds) then
104
          snd props.showSidePanel $ \_ -> GET.Opened GET.SideTabData
105 106 107
        else
          pure unit

108 109
      -- Timer to turn off the initial FA. This is because FA eats up lot of
      -- CPU, has memory leaks etc.
110
      R.useEffect1' (fst props.forceAtlasState) $ do
111
        if (fst props.forceAtlasState) == SigmaxT.InitialRunning then do
112
          timeoutId <- setTimeout 9000 $ do
113 114
            let (toggled /\ setToggled) = props.forceAtlasState
            case toggled of
115
              SigmaxT.InitialRunning -> setToggled $ const SigmaxT.Paused
116 117 118 119 120 121
              _ -> pure unit
            R.setRef mFAPauseRef Nothing
          R.setRef mFAPauseRef $ Just timeoutId
          pure unit
         else
           pure unit
122

123
      let edgesConfluenceSorted = A.sortWith (_.confluence) $ Seq.toUnfoldable $ SigmaxT.graphEdges props.graph
124 125 126 127
      let edgeConfluenceMin = maybe 0.0 _.confluence $ A.head edgesConfluenceSorted
      let edgeConfluenceMax = maybe 100.0 _.confluence $ A.last edgesConfluenceSorted
      let edgeConfluenceRange = Range.Closed { min: edgeConfluenceMin, max: edgeConfluenceMax }

128
      --let edgesWeightSorted = A.sortWith (_.weight) $ Seq.toUnfoldable $ SigmaxT.graphEdges props.graph
129 130 131 132 133
      --let edgeWeightMin = maybe 0.0 _.weight $ A.head edgesWeightSorted
      --let edgeWeightMax = maybe 100.0 _.weight $ A.last edgesWeightSorted
      --let edgeWeightRange = Range.Closed { min: edgeWeightMin, max: edgeWeightMax }
      let edgeWeightRange = Range.Closed {
           min: 0.0
134
         , max: I.toNumber $ Seq.length $ SigmaxT.graphEdges props.graph
135
         }
136

137
      let nodesSorted = A.sortWith (_.size) $ Seq.toUnfoldable $ SigmaxT.graphNodes props.graph
138 139 140 141
      let nodeSizeMin = maybe 0.0 _.size $ A.head nodesSorted
      let nodeSizeMax = maybe 100.0 _.size $ A.last nodesSorted
      let nodeSizeRange = Range.Closed { min: nodeSizeMin, max: nodeSizeMax }

142 143
      pure $ case getShowControls props of
        false -> RH.div {} []
144
        true -> RH.div {}
145 146 147
            [ R2.menu { id: "toolbar" }
              [ RH.ul {}
                [ -- change type button (?)
148
                  RH.li {} [ centerButton props.sigmaRef ]
149
                , RH.li {} [ pauseForceAtlasButton {state: props.forceAtlasState} ]
150
                , RH.li {} [ edgesToggleButton {state: props.showEdges} ]
151
                , RH.li {} [ louvainToggleButton props.showLouvain ]
152 153
                , RH.li {} [ edgeConfluenceControl edgeConfluenceRange props.edgeConfluence ]
                , RH.li {} [ edgeWeightControl edgeWeightRange props.edgeWeight ]
154 155 156 157 158
                  -- change level
                  -- file upload
                  -- run demo
                  -- search button
                  -- search topics
159
                , RH.li {} [ labelSizeButton props.sigmaRef localControls.labelSize ] -- labels size: 1-4
160
                , RH.li {} [ nodeSizeControl nodeSizeRange props.nodeSize ]
161
                  -- zoom: 0 -100 - calculate ratio
162
                , RH.li {} [ multiSelectEnabledButton props.multiSelectEnabled ]  -- toggle multi node selection
163
                  -- save button
164
                , RH.li {} [ nodeSearchControl { graph: props.graph
165
                                               , multiSelectEnabled: props.multiSelectEnabled
166
                                               , selectedNodeIds: props.selectedNodeIds } ]
167
                , RH.li {} [ mouseSelectorSizeButton props.sigmaRef localControls.mouseSelectorSize ]
168
                , RH.li {} [ cameraButton { id: props.graphId
169
                                          , hyperdataGraph: props.hyperdataGraph
170
                                          , session: props.session
171 172
                                          , sigmaRef: props.sigmaRef
                                          , treeReload: props.treeReload } ]
173
                ]
James Laver's avatar
James Laver committed
174 175 176
              ]
            ]

177 178 179 180 181 182
useGraphControls :: { forceAtlasS :: SigmaxT.ForceAtlasState
                   , graph :: SigmaxT.SGraph
                   , graphId :: GET.GraphId
                   , hyperdataGraph :: GET.HyperdataGraph
                   , session :: Session
                   , treeReload :: Unit -> Effect Unit }
183
                 -> R.Hooks (Record Controls)
184 185 186 187 188 189
useGraphControls { forceAtlasS
                 , graph
                 , graphId
                 , hyperdataGraph
                 , session
                 , treeReload } = do
190
  edgeConfluence <- R.useState' $ Range.Closed { min: 0.0, max: 1.0 }
191 192
  edgeWeight <- R.useState' $ Range.Closed {
      min: 0.0
193
    , max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph
194
    }
195
  forceAtlasState <- R.useState' forceAtlasS
196
  graphStage      <- R.useState' Graph.Init
197
  multiSelectEnabled <- R.useState' false
198
  nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 100.0 }
199 200
  removedNodeIds <- R.useState' SigmaxT.emptyNodeIds
  selectedNodeIds <- R.useState' SigmaxT.emptyNodeIds
201
  showControls    <- R.useState' false
202
  showEdges <- R.useState' SigmaxT.EShow
203
  showLouvain <- R.useState' false
204
  showSidePanel   <- R.useState' GET.InitialClosed
205
  showTree <- R.useState' false
206 207
  sigma <- Sigmax.initSigma
  sigmaRef <- R.useRef sigma
208

209
  pure { edgeConfluence
210
       , edgeWeight
211
       , forceAtlasState
212
       , graph
213
       , graphId
214
       , graphStage
215
       , hyperdataGraph
216
       , multiSelectEnabled
217
       , nodeSize
218
       , removedNodeIds
219
       , selectedNodeIds
220
       , session
221
       , showControls
222
       , showEdges
223
       , showLouvain
224 225 226
       , showSidePanel
       , showTree
       , sigmaRef
227
       , treeReload
228
       }
229 230

getShowControls :: Record Controls -> Boolean
James Laver's avatar
James Laver committed
231 232
getShowControls { showControls: ( should /\ _ ) } = should

233 234 235
getShowTree :: Record Controls -> Boolean
getShowTree { showTree: ( should /\ _ ) } = should

236 237
setShowControls :: Record Controls -> Boolean -> Effect Unit
setShowControls { showControls: ( _ /\ set ) } v = set $ const v
James Laver's avatar
James Laver committed
238

239
setShowTree :: Record Controls -> Boolean -> Effect Unit
240
setShowTree { showTree: ( _ /\ set ) } v = set $ not <<< const v