Controls.purs 11.8 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
 ) where

8
import Data.Array as A
9
import Data.Int as I
10 11
import Data.Maybe (Maybe(..), maybe)
import Data.Sequence as Seq
12
import Data.Set as Set
13
import Effect.Timer (setTimeout)
14
import Prelude
James Laver's avatar
James Laver committed
15 16
import Reactix as R
import Reactix.DOM.HTML as RH
17
import Toestand as T
James Laver's avatar
James Laver committed
18

19
import Gargantext.Components.Graph as Graph
20
import Gargantext.Components.GraphExplorer.Button (centerButton, cameraButton)
21
import Gargantext.Components.GraphExplorer.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl)
22
import Gargantext.Components.GraphExplorer.SlideButton (labelSizeButton, mouseSelectorSizeButton)
Alexandre Delanoë's avatar
Alexandre Delanoë committed
23
import Gargantext.Components.GraphExplorer.ToggleButton (multiSelectEnabledButton, edgesToggleButton, louvainToggleButton, pauseForceAtlasButton{-, resetForceAtlasButton-})
24
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
25
import Gargantext.Components.GraphExplorer.Types as GET
26
import Gargantext.Hooks.Sigmax as Sigmax
27
import Gargantext.Hooks.Sigmax.Types as SigmaxT
28
import Gargantext.Sessions (Session)
29
import Gargantext.Types as GT
30
import Gargantext.Utils.Range as Range
31
import Gargantext.Utils.Reactix as R2
32
import Gargantext.Utils.Toestand as T2
33

34 35
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Controls"
36

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

60
type LocalControls = ( labelSize :: T.Box Number, mouseSelectorSize :: T.Box Number )
61 62 63

initialLocalControls :: R.Hooks (Record LocalControls)
initialLocalControls = do
64 65 66
  labelSize <- T.useBox 14.0
  mouseSelectorSize <- T.useBox 15.0
  pure $ { labelSize, mouseSelectorSize }
67

68 69
controls :: R2.Component Controls
controls = R.createElement controlsCpt
James Laver's avatar
James Laver committed
70
controlsCpt :: R.Component Controls
71
controlsCpt = here.component "controls" cpt
James Laver's avatar
James Laver committed
72
  where
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    cpt { edgeConfluence
        , edgeWeight
        , forceAtlasState
        , graph
        , graphId
        , graphStage
        , hyperdataGraph
        , multiSelectEnabled
        , nodeSize
        , reloadForest
        , selectedNodeIds
        , session
        , showControls
        , showEdges
        , showLouvain
88
        , sidePanelState
89
        , sideTab
90 91 92 93 94
        , sigmaRef } _ = do
      forceAtlasState' <- T.useLive T.unequal forceAtlasState
      graphStage' <- T.useLive T.unequal graphStage
      selectedNodeIds' <- T.useLive T.unequal selectedNodeIds
      showControls' <- T.useLive T.unequal showControls
95
      sidePanelState' <- T.useLive T.unequal sidePanelState
96

97
      localControls <- initialLocalControls
98 99
      -- ref to track automatic FA pausing
      -- If user pauses FA before auto is triggered, clear the timeoutId
100
      mFAPauseRef <- R.useRef Nothing
101

102 103
      -- When graph is changed, cleanup the mFAPauseRef so that forceAtlas
      -- timeout is retriggered.
104
      R.useEffect' $ do
105
        case graphStage' of
106 107 108
          Graph.Init -> R.setRef mFAPauseRef Nothing
          _          -> pure unit

109
      -- Handle case when FA is paused from outside events, eg. the automatic timer.
110
      R.useEffect' $ Sigmax.handleForceAtlas2Pause sigmaRef forceAtlasState mFAPauseRef Graph.forceAtlas2Settings
111

112
      -- Handle automatic edge hiding when FA is running (to prevent flickering).
113
      -- TODO Commented temporarily: this breaks forceatlas rendering after reset
114 115 116
      -- NOTE This is a hack anyways. It's force atlas that should be fixed.
      R.useEffect2' sigmaRef forceAtlasState' $ do
        T.modify_ (SigmaxT.forceAtlasEdgeState forceAtlasState') showEdges
117 118

      -- Automatic opening of sidebar when a node is selected (but only first time).
119
      R.useEffect' $ do
120
        if sidePanelState' == GT.InitialClosed && (not Set.isEmpty selectedNodeIds') then do
121
          T.write_ GT.Opened sidePanelState
122
          T.write_ GET.SideTabData sideTab
123 124 125
        else
          pure unit

126 127
      -- Timer to turn off the initial FA. This is because FA eats up lot of
      -- CPU, has memory leaks etc.
128 129
      R.useEffect1' forceAtlasState' $ do
        if forceAtlasState' == SigmaxT.InitialRunning then do
130
          timeoutId <- setTimeout 9000 $ do
131 132
            case forceAtlasState' of
              SigmaxT.InitialRunning ->
133
                T.write_ SigmaxT.Paused forceAtlasState
134 135 136 137 138 139
              _ -> pure unit
            R.setRef mFAPauseRef Nothing
          R.setRef mFAPauseRef $ Just timeoutId
          pure unit
         else
           pure unit
140

141
      let edgesConfluenceSorted = A.sortWith (_.confluence) $ Seq.toUnfoldable $ SigmaxT.graphEdges graph
142 143 144 145
      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 }

146
      --let edgesWeightSorted = A.sortWith (_.weight) $ Seq.toUnfoldable $ SigmaxT.graphEdges graph
147 148 149 150 151
      --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
152
         , max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph
153
         }
154

155
      let nodesSorted = A.sortWith (_.size) $ Seq.toUnfoldable $ SigmaxT.graphNodes graph
156 157 158 159
      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 }

160 161 162
      let className = "navbar navbar-expand-lg " <> if showControls' then "" else "d-none"

      pure $ RH.nav { className }
163 164 165
                 [ RH.ul { className: "navbar-nav mx-auto" }
                   [ -- change type button (?)
                     navItem [ centerButton sigmaRef ]
166
                   -- , navItem [ resetForceAtlasButton { forceAtlasState, sigmaRef } [] ]
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
                   , navItem [ pauseForceAtlasButton { state: forceAtlasState } [] ]
                   , navItem [ edgesToggleButton { state: showEdges } [] ]
                   , navItem [ louvainToggleButton { state: showLouvain } [] ]
                   , navItem [ edgeConfluenceControl { range: edgeConfluenceRange
                                                     , state: edgeConfluence } [] ]
                   , navItem [ edgeWeightControl { range: edgeWeightRange
                                                 , state: edgeWeight } [] ]
                   -- change level
                   -- file upload
                   -- run demo
                   -- search button
                   -- search topics
                   , navItem [ labelSizeButton sigmaRef localControls.labelSize ] -- labels size: 1-4
                   , navItem [ nodeSizeControl { range: nodeSizeRange
                                               , state: nodeSize } [] ]
                   -- zoom: 0 -100 - calculate ratio
                   , navItem [ multiSelectEnabledButton { state: multiSelectEnabled } [] ]  -- toggle multi node selection
                   -- save button
                   , navItem [ mouseSelectorSizeButton sigmaRef localControls.mouseSelectorSize ]
                   , navItem [ cameraButton { id: graphId
                                            , hyperdataGraph: hyperdataGraph
                                            , session: session
                                            , sigmaRef: sigmaRef
                                            , reloadForest } ]
                   ]
                 ]
      where
        navItem = RH.li { className: "nav-item" }
195
          --   RH.ul {} [ -- change type button (?)
196 197 198 199 200 201
          --     RH.li {} [ centerButton sigmaRef ]
          --   , RH.li {} [ pauseForceAtlasButton {state: forceAtlasState} ]
          --   , RH.li {} [ edgesToggleButton {state: showEdges} ]
          --   , RH.li {} [ louvainToggleButton showLouvain ]
          --   , RH.li {} [ edgeConfluenceControl edgeConfluenceRange edgeConfluence ]
          --   , RH.li {} [ edgeWeightControl edgeWeightRange edgeWeight ]
202 203 204 205 206
          --     -- change level
          --     -- file upload
          --     -- run demo
          --     -- search button
          --     -- search topics
207 208
          --   , RH.li {} [ labelSizeButton sigmaRef localControls.labelSize ] -- labels size: 1-4
          --   , RH.li {} [ nodeSizeControl nodeSizeRange nodeSize ]
209
          --     -- zoom: 0 -100 - calculate ratio
210
          --   , RH.li {} [ multiSelectEnabledButton multiSelectEnabled ]  -- toggle multi node selection
211
          --     -- save button
212 213 214
          --   , RH.li {} [ nodeSearchControl { graph: graph
          --                                  , multiSelectEnabled: multiSelectEnabled
          --                                  , selectedNodeIds: selectedNodeIds } ]
215 216 217 218 219 220
          --   , RH.li {} [ mouseSelectorSizeButton sigmaRef localControls.mouseSelectorSize ]
          --   , RH.li {} [ cameraButton { id: graphId
          --                             , hyperdataGraph: hyperdataGraph
          --                             , session: session
          --                             , sigmaRef: sigmaRef
          --                             , reloadForest: reloadForest } ]
221 222
          --   ]
          -- ]
James Laver's avatar
James Laver committed
223

224
useGraphControls :: { forceAtlasS :: SigmaxT.ForceAtlasState
225 226 227 228 229 230 231 232
                    , graph          :: SigmaxT.SGraph
                    , graphId        :: GET.GraphId
                    , hyperdataGraph :: GET.HyperdataGraph
                    , reloadForest   :: T2.ReloadS
                    , session        :: Session
                    , showTree       :: T.Box Boolean
                    , sidePanel      :: T.Box (Maybe (Record GEST.SidePanel))
                    , sidePanelState :: T.Box GT.SidePanelState }
233
                 -> R.Hooks (Record Controls)
234 235 236 237
useGraphControls { forceAtlasS
                 , graph
                 , graphId
                 , hyperdataGraph
238
                 , reloadForest
239
                 , session
240
                 , showTree
241 242
                 , sidePanel
                 , sidePanelState } = do
243 244
  edgeConfluence <- T.useBox $ Range.Closed { min: 0.0, max: 1.0 }
  edgeWeight <- T.useBox $ Range.Closed {
245
      min: 0.0
246
    , max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph
247
    }
248 249 250 251 252
  forceAtlasState <- T.useBox forceAtlasS
  graphStage      <- T.useBox Graph.Init
  nodeSize <- T.useBox $ Range.Closed { min: 0.0, max: 100.0 }
  showEdges <- T.useBox SigmaxT.EShow
  showLouvain <- T.useBox false
253 254
  sigma <- Sigmax.initSigma
  sigmaRef <- R.useRef sigma
255

256
  { multiSelectEnabled, removedNodeIds, selectedNodeIds, showControls, sideTab } <- GEST.focusedSidePanel sidePanel
257

258
  pure { edgeConfluence
259
       , edgeWeight
260
       , forceAtlasState
261
       , graph
262
       , graphId
263
       , graphStage
264
       , hyperdataGraph
265
       , multiSelectEnabled
266
       , nodeSize
267
       , removedNodeIds
268
       , selectedNodeIds
269
       , session
270
       , showControls
271
       , showEdges
272
       , showLouvain
273
       , sidePanelState
274
       , showTree
275
       , sideTab
276
       , sigmaRef
James Laver's avatar
James Laver committed
277
       , reloadForest
278
       }