Sidebar.purs 20.7 KB
Newer Older
1
module Gargantext.Components.GraphExplorer.Sidebar
arturo's avatar
arturo committed
2
  ( sidebar
arturo's avatar
arturo committed
3
  ) where
4

5 6
import Gargantext.Prelude

7
import Control.Parallel (parTraverse)
arturo's avatar
arturo committed
8
import Data.Array (last, mapWithIndex)
9
import Data.Array as A
10
import Data.Either (Either(..))
arturo's avatar
arturo committed
11
import Data.Foldable (intercalate)
12
import Data.Foldable as F
13
import Data.Int (fromString)
14
import Data.Map as Map
15
import Data.Maybe (Maybe(..), fromJust)
16
import Data.Sequence as Seq
17
import Data.Set as Set
arturo's avatar
arturo committed
18
import Data.Tuple.Nested ((/\))
19
import Effect (Effect)
20
import Effect.Aff (launchAff_)
21
import Effect.Class (liftEffect)
arturo's avatar
arturo committed
22
import Gargantext.Components.App.Store as AppStore
arturo's avatar
arturo committed
23 24
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
arturo's avatar
arturo committed
25 26
import Gargantext.Components.GraphExplorer.Sidebar.ContactList (contactListWrapper)
import Gargantext.Components.GraphExplorer.Sidebar.DocList (docListWrapper)
arturo's avatar
arturo committed
27 28
import Gargantext.Components.GraphExplorer.Sidebar.Legend as Legend
import Gargantext.Components.GraphExplorer.Store as GraphStore
29 30
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.Lang (Lang(..))
31
import Gargantext.Core.NgramsTable.Functions as NTC
32
import Gargantext.Config.REST (AffRESTError)
33
import Gargantext.Core.NgramsTable.Types as CNT
34
import Gargantext.Data.Array (mapMaybe)
35
import Gargantext.Ends (Frontends)
36
import Gargantext.Hooks.FirstEffect (useFirstEffect')
37
import Gargantext.Hooks.Sigmax.Types as SigmaxT
38
import Gargantext.Sessions (Session)
39
import Gargantext.Types (CTabNgramType, FrontendError(..), NodeID, TabSubType(..), TabType(..), TermList(..), modeTabType)
40
import Gargantext.Utils (nbsp, setter, (?))
41
import Gargantext.Utils.Reactix as R2
42
import Gargantext.Utils.Toestand as T2
43
import Math as Math
44 45 46 47 48
import Partial.Unsafe (unsafePartial)
import Reactix as R
import Reactix.DOM.HTML as H
import Record as Record
import Toestand as T
49

50 51
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Sidebar"
52

arturo's avatar
arturo committed
53
type Props =
arturo's avatar
arturo committed
54
  ( metaData        :: GET.MetaData
55
  , session         :: Session
arturo's avatar
arturo committed
56
  , frontends       :: Frontends
57
  )
58

arturo's avatar
arturo committed
59 60
sidebar :: R2.Leaf Props
sidebar = R2.leaf sidebarCpt
61

arturo's avatar
arturo committed
62 63 64 65 66
sidebarCpt :: R.Component Props
sidebarCpt = here.component "sidebar" cpt where
  cpt props _ = do
    -- States
    { sideTab
arturo's avatar
arturo committed
67
    } <- GraphStore.use
68

arturo's avatar
arturo committed
69
    sideTab'  <- R2.useLive' sideTab
70

arturo's avatar
arturo committed
71 72 73 74 75 76
    -- Computed
    let
      sideTabs =
        [ GET.SideTabLegend
        , GET.SideTabData
        , GET.SideTabCommunity
arturo's avatar
arturo committed
77 78
        ]

arturo's avatar
arturo committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    -- Render
    pure $

      H.div
      { className: "graph-sidebar" }
      [
        -- Menu
        B.tabs
        { value: sideTab'
        , list: sideTabs
        , callback: flip T.write_ sideTab
        }
      ,
        case sideTab' of
          GET.SideTabLegend     -> sideTabLegend props
          GET.SideTabData       -> sideTabData props
          GET.SideTabCommunity  -> sideTabCommunity props
      ]

arturo's avatar
arturo committed
98 99 100 101
------------------------------------------------------------

sideTabLegend :: R2.Leaf Props
sideTabLegend = R2.leaf sideTabLegendCpt
arturo's avatar
arturo committed
102

103
sideTabLegendCpt :: R.Component Props
104 105
sideTabLegendCpt = here.component "sideTabLegend" cpt
  where
arturo's avatar
arturo committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    cpt { metaData: GET.MetaData { legend } } _ = pure $

      H.div
      { className: "graph-sidebar__legend-tab" }
      [
        Legend.legend
        { items: Seq.fromFoldable legend }
      ,
        H.hr {}
      ,
        documentation EN
      ]

------------------------------------------------------------

sideTabData :: R2.Leaf Props
sideTabData = R2.leaf sideTabDataCpt
arturo's avatar
arturo committed
123

124
sideTabDataCpt :: R.Component Props
arturo's avatar
arturo committed
125 126 127 128 129
sideTabDataCpt = here.component "sideTabData" cpt where
  cpt props _ = do
    -- States
    { selectedNodeIds
    , graph
arturo's avatar
arturo committed
130
    } <- GraphStore.use
arturo's avatar
arturo committed
131 132 133

    selectedNodeIds'  <- R2.useLive' selectedNodeIds
    graph'            <- R2.useLive' graph
134

arturo's avatar
arturo committed
135 136 137
    -- Computed
    let
      hasSelection = not $ Set.isEmpty selectedNodeIds'
arturo's avatar
arturo committed
138

arturo's avatar
arturo committed
139 140
    -- Render
    pure $
arturo's avatar
arturo committed
141

arturo's avatar
arturo committed
142 143 144 145
      H.div
      { className: "graph-sidebar__data-tab" }
      [
        case hasSelection of
arturo's avatar
arturo committed
146

arturo's avatar
arturo committed
147 148
          -- No result
          false ->
arturo's avatar
arturo committed
149

arturo's avatar
arturo committed
150 151 152 153 154
            B.caveat
            {}
            [
              H.text "Select one or more nodes to get their informations"
            ]
arturo's avatar
arturo committed
155

arturo's avatar
arturo committed
156 157
          -- Nodes have been selected
          true ->
arturo's avatar
arturo committed
158

arturo's avatar
arturo committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172
            R.fragment
            [
              selectedNodes $
              { nodesMap: SigmaxT.nodesGraphMap graph'
              } `Record.merge` props
            ,
              sideBarTabSeparator
            ,
              neighborhood
              {}
            ,
              sideBarTabSeparator
            ,
              docListWrapper
arturo's avatar
arturo committed
173
              { metaData: props.metaData
arturo's avatar
arturo committed
174 175 176
              }
            ]
      ]
177

arturo's avatar
arturo committed
178
------------------------------------------------------------
179

arturo's avatar
arturo committed
180 181
sideTabCommunity :: R2.Leaf Props
sideTabCommunity = R2.leaf sideTabCommunityCpt
arturo's avatar
arturo committed
182

183
sideTabCommunityCpt :: R.Component Props
arturo's avatar
arturo committed
184
sideTabCommunityCpt = here.component "sideTabCommunity" cpt where
arturo's avatar
arturo committed
185
  cpt props _ = do
arturo's avatar
arturo committed
186 187 188
    -- States
    { selectedNodeIds
    , graph
arturo's avatar
arturo committed
189
    } <- GraphStore.use
190

arturo's avatar
arturo committed
191 192
    selectedNodeIds'  <- R2.useLive' selectedNodeIds
    graph'            <- R2.useLive' graph
arturo's avatar
arturo committed
193

arturo's avatar
arturo committed
194 195 196
    -- Computed
    let
      hasSelection = not $ Set.isEmpty selectedNodeIds'
arturo's avatar
arturo committed
197

arturo's avatar
arturo committed
198 199
    -- Render
    pure $
arturo's avatar
arturo committed
200

arturo's avatar
arturo committed
201 202 203 204
      H.div
      { className: "graph-sidebar__community-tab" }
      [
        case hasSelection of
arturo's avatar
arturo committed
205

arturo's avatar
arturo committed
206 207
          -- No result
          false ->
arturo's avatar
arturo committed
208

arturo's avatar
arturo committed
209 210 211 212 213
            B.caveat
            {}
            [
              H.text "Select one or more nodes to get their informations"
            ]
arturo's avatar
arturo committed
214

arturo's avatar
arturo committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
          -- Nodes have been selection
          true ->

            R.fragment
            [
              selectedNodes $
              { nodesMap: SigmaxT.nodesGraphMap graph'
              } `Record.merge` props
            ,
              sideBarTabSeparator
            ,
              neighborhood
              {}
            ,
              sideBarTabSeparator
            ,
arturo's avatar
arturo committed
231 232
              contactListWrapper
              { metaData: props.metaData
arturo's avatar
arturo committed
233 234 235
              }
            ]
      ]
236

arturo's avatar
arturo committed
237 238 239 240 241 242 243 244
-------------------------------------------

sideBarTabSeparator :: R.Element
sideBarTabSeparator =
  H.div
  { className: "graph-sidebar__separator" }
  [
    B.icon
arturo's avatar
arturo committed
245
    { name: "angle-double-down" }
arturo's avatar
arturo committed
246
  ]
247 248

-------------------------------------------
249 250
-- TODO
-- selectedNodes :: Record Props -> Map.Map String Nodes -> R.Element
251

arturo's avatar
arturo committed
252 253
type SelectedNodesProps =
  ( nodesMap :: SigmaxT.NodesMap
254 255 256
  | Props
  )

arturo's avatar
arturo committed
257 258
selectedNodes :: R2.Leaf SelectedNodesProps
selectedNodes = R2.leaf selectedNodesCpt
arturo's avatar
arturo committed
259

260
selectedNodesCpt :: R.Component SelectedNodesProps
arturo's avatar
arturo committed
261
selectedNodesCpt = here.component "selectedNodes" cpt where
arturo's avatar
arturo committed
262
  cpt props _ = do
263 264
    -- | States
    -- |
arturo's avatar
arturo committed
265 266
    { selectedNodeIds
    , graph
267
    , expandSelection
arturo's avatar
arturo committed
268
    } <- GraphStore.use
arturo's avatar
arturo committed
269

arturo's avatar
arturo committed
270 271
    selectedNodeIds'    <- R2.useLive' selectedNodeIds
    graph'              <- R2.useLive' graph
272 273 274 275 276 277 278 279
    expandSelection'    <- R2.useLive' expandSelection

    -- | Effects
    -- |

    -- transfer local Component change to Local Storage cache
    useFirstEffect' $
      flip T.listen expandSelection onExpandSelectionChange
arturo's avatar
arturo committed
280

281 282
    -- | Behaviors
    -- |
arturo's avatar
arturo committed
283 284 285
    let
      onBadgeClick id _ = T.write_ (Set.singleton id) selectedNodeIds

286
      onExpandClick _ = T.modify_ (not) expandSelection
arturo's avatar
arturo committed
287

288 289
    -- | Render
    -- |
arturo's avatar
arturo committed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    pure $

      H.ul
      { className: intercalate " "
          [ "graph-selected-nodes"
          , "list-group"
          ]
        }
      [
        H.li
        { className: "list-group-item" }
        [
          H.ul
          {} $

          Seq.toUnfoldable $
arturo's avatar
arturo committed
306
            flip Seq.map (badges graph' selectedNodeIds') \node ->
arturo's avatar
arturo committed
307 308 309 310 311 312 313 314 315 316 317 318

              H.li
              { className: "graph-selected-nodes__item" }
              [
                H.a
                { className: intercalate " "
                    [ "graph-selected-nodes__badge"
                    , "badge badge-info"
                    ]
                , on: { click: onBadgeClick node.id }
                }
                [ H.text node.label ]
Alexandre Delanoë's avatar
Alexandre Delanoë committed
319
              ]
arturo's avatar
arturo committed
320 321 322
        ,
          -- Expand NGrams actions
          B.iconButton
323
          { name: expandSelection' ?
arturo's avatar
arturo committed
324 325 326 327 328
              "caret-up" $
              "caret-down"
          , className: "graph-selected-nodes__expand"
          , callback: onExpandClick
          }
arturo's avatar
arturo committed
329 330
        ]
      ,
arturo's avatar
arturo committed
331
        -- NGrams actions
332
        R2.when expandSelection' $
arturo's avatar
arturo committed
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372

          H.li
          { className: intercalate " "
              [ "list-group-item"
              , "graph-selected-nodes__actions"
              ]
          }
          [
            B.buttonGroup
            { collapse: false }
            [
              updateTermButton
              ( props `Record.merge`
                { variant: ButtonVariant Light
                , rType: CandidateTerm
                }
              )
              [
                B.icon
                { name: "circle"
                , className: "mr-1 candidate-term"
                }
              ,
                H.text "Move as candidate"
              ]
            ,
              updateTermButton
              ( props `Record.merge`
                { variant: ButtonVariant Light
                , rType: StopTerm
                }
              )
              [
                B.icon
                { name: "circle"
                , className: "mr-1 stop-term"
                }
              ,
                H.text "Move as stop"
              ]
Alexandre Delanoë's avatar
Alexandre Delanoë committed
373
            ]
arturo's avatar
arturo committed
374
          ]
arturo's avatar
arturo committed
375 376
      ]

377 378 379 380 381 382
onExpandSelectionChange :: T.Change Boolean -> Effect Unit
onExpandSelectionChange { new } = do
  cache <- R2.loadLocalStorageState' R2.graphParamsKey GET.defaultCacheParams
  let update = setter (_ { expandSelection = new }) cache
  R2.setLocalStorageState R2.graphParamsKey update

arturo's avatar
arturo committed
383 384
---------------------------------------------------------

arturo's avatar
arturo committed
385
neighborhood :: R2.Leaf ()
arturo's avatar
arturo committed
386
neighborhood = R2.leaf neighborhoodCpt
arturo's avatar
arturo committed
387 388

neighborhoodCpt :: R.Memo ()
arturo's avatar
arturo committed
389
neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where
arturo's avatar
arturo committed
390
  cpt _ _ = do
391 392
    -- | States
    -- |
arturo's avatar
arturo committed
393 394
    { selectedNodeIds
    , graph
395
    , expandNeighborhood
arturo's avatar
arturo committed
396
    } <- GraphStore.use
arturo's avatar
arturo committed
397 398

    selectedNodeIds' <-
arturo's avatar
arturo committed
399 400
      R2.useLive' selectedNodeIds

401 402
    expandNeighborhood' <-
      R2.useLive' expandNeighborhood
arturo's avatar
arturo committed
403

arturo's avatar
arturo committed
404 405
    graph' <-
      R2.useLive' graph
arturo's avatar
arturo committed
406 407 408

    showMore /\ showMoreBox <-
      R2.useBox' false
arturo's avatar
arturo committed
409

arturo's avatar
arturo committed
410 411
    termList /\ termListBox <-
      R2.useBox' []
arturo's avatar
arturo committed
412

arturo's avatar
arturo committed
413 414
    termCount /\ termCountBox <-
      R2.useBox' 0
arturo's avatar
arturo committed
415

416 417
    -- | Computed
    -- |
arturo's avatar
arturo committed
418
    let
arturo's avatar
arturo committed
419
      minSize = F.foldl Math.min 0.0 (Seq.map _.size (SigmaxT.graphNodes graph'))
arturo's avatar
arturo committed
420

arturo's avatar
arturo committed
421
      maxSize = F.foldl Math.max 0.0 (Seq.map _.size (SigmaxT.graphNodes graph'))
arturo's avatar
arturo committed
422 423 424

      maxTruncateResult = 5

arturo's avatar
arturo committed
425
      withTruncateResults = (termCount > maxTruncateResult) && (not showMore)
arturo's avatar
arturo committed
426 427


428 429
    -- | Behaviors
    -- |
arturo's avatar
arturo committed
430 431 432
    let
      onBadgeClick id _ = T.write_ (Set.singleton id) selectedNodeIds

433 434 435 436 437 438 439 440
      onExpandClick _ = T.modify_ (not) expandNeighborhood

    -- | Effects
    -- |

    -- transfer local Component change to Local Storage cache
    useFirstEffect' $
      flip T.listen expandNeighborhood onExpandNeighborhoodChange
arturo's avatar
arturo committed
441

arturo's avatar
arturo committed
442
    R.useEffect1' selectedNodeIds' do
arturo's avatar
arturo committed
443
      let refreshed = neighbourBadges graph' selectedNodeIds'
arturo's avatar
arturo committed
444 445 446 447
      let count     = Seq.length refreshed
      let ordered   = A.sortWith (\n -> -n.size) $ Seq.toUnfoldable refreshed
      T.write_ count   termCountBox
      T.write_ ordered termListBox
arturo's avatar
arturo committed
448 449
      T.write_ false showMoreBox

450 451
    -- | Render
    -- |
arturo's avatar
arturo committed
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
    pure $

      H.ul
      { className: intercalate " "
          [ "graph-neighborhood"
          , "list-group"
          ]
      }
      [
        -- Extracted count
        H.li
        { className: "list-group-item" }
        [
          -- @XXX: Bootstrap CSS w/ one <li> deduped the list-style-type bullet
          H.div
          { className: "graph-neighborhood__counter" }
          [
arturo's avatar
arturo committed
469 470
            B.wad'
            [ "text-info", "d-inline" ] $
arturo's avatar
arturo committed
471
            show termCount
arturo's avatar
arturo committed
472 473
          ,
            H.text $ nbsp 1 <> "terms"
arturo's avatar
arturo committed
474 475 476
          ,
            -- Expand word cloud
            B.iconButton
477
            { name: expandNeighborhood' ?
arturo's avatar
arturo committed
478 479 480 481 482
                "caret-up" $
                "caret-down"
            , className: "graph-neighborhood__expand"
            , callback: onExpandClick
            }
483 484
          ]
        ]
arturo's avatar
arturo committed
485 486
      ,
        -- Word cloud
487
        R2.when expandNeighborhood' $
arturo's avatar
arturo committed
488

arturo's avatar
arturo committed
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
          H.li
          { className: "list-group-item"}
          [
            H.ul
            {} $
            flip mapWithIndex termList \index node ->

              R2.when
              (
                withTruncateResults == false
              || index < maxTruncateResult
              ) $
                H.li
                { className: "graph-neighborhood__badge" }
                [
                  H.a
                  { className: "badge badge-light"
                  -- adjust font accordingly
                  , style:
                      { fontSize: badgeSize
                          minSize
                          maxSize
                          node.size
                      , lineHeight: badgeSize
                          minSize
                          maxSize
                          node.size
                      }
                  , on: { click: onBadgeClick node.id }
                  }
                  [ H.text node.label ]
                ]
          ,
            R2.when (withTruncateResults) $

              B.button
              { variant: ButtonVariant Light
              , callback: \_ -> T.modify_ (not) showMoreBox
              , block: true
              , className: "graph-neighborhood__show-more"
              }
arturo's avatar
arturo committed
530
              [
arturo's avatar
arturo committed
531
                H.text "Show more"
arturo's avatar
arturo committed
532
              ]
arturo's avatar
arturo committed
533
          ]
arturo's avatar
arturo committed
534
      ]
535

536 537 538 539 540 541
onExpandNeighborhoodChange :: T.Change Boolean -> Effect Unit
onExpandNeighborhoodChange { new } = do
  cache <- R2.loadLocalStorageState' R2.graphParamsKey GET.defaultCacheParams
  let update = setter (_ { expandNeighborhood = new }) cache
  R2.setLocalStorageState R2.graphParamsKey update

arturo's avatar
arturo committed
542
---------------------------------------------------------
543

arturo's avatar
arturo committed
544 545
type UpdateTermButtonProps =
  ( variant    :: ButtonVariant
546 547
  , nodesMap   :: SigmaxT.NodesMap
  , rType      :: TermList
arturo's avatar
arturo committed
548
  | Props
549 550
  )

551
updateTermButton :: R2.Component UpdateTermButtonProps
arturo's avatar
arturo committed
552
updateTermButton = R2.component updateTermButtonCpt
arturo's avatar
arturo committed
553

554
updateTermButtonCpt :: R.Component UpdateTermButtonProps
arturo's avatar
arturo committed
555
updateTermButtonCpt = here.component "updateTermButton" cpt where
arturo's avatar
arturo committed
556
  cpt { variant
arturo's avatar
arturo committed
557 558 559 560 561 562
      , metaData
      , nodesMap
      , rType
      , session
      } children = do
    -- States
arturo's avatar
arturo committed
563 564 565 566
    { errors
    , reloadForest
    } <- AppStore.use

arturo's avatar
arturo committed
567 568 569
    { removedNodeIds
    , selectedNodeIds
    , graphId
arturo's avatar
arturo committed
570
    } <- GraphStore.use
arturo's avatar
arturo committed
571 572 573

    selectedNodeIds' <- R2.useLive' selectedNodeIds
    graphId'         <- R2.useLive' graphId
arturo's avatar
arturo committed
574 575 576 577 578 579 580

    -- Behaviors
    let
      callback _ = do
        let nodes = mapMaybe (\id -> Map.lookup id nodesMap)
                            $ Set.toUnfoldable selectedNodeIds'
        sendPatches { errors
arturo's avatar
arturo committed
581
                    , graphId: graphId'
arturo's avatar
arturo committed
582 583 584 585
                    , metaData: metaData
                    , nodes
                    , session: session
                    , termList: rType
arturo's avatar
arturo committed
586 587
                    , reloadForest
                    }
arturo's avatar
arturo committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
        T.write_ selectedNodeIds' removedNodeIds
        T.write_ SigmaxT.emptyNodeIds selectedNodeIds

    -- Render
    pure $

      B.button
      { variant
      , callback
      }
      children


---------------------------------------------------------

badgeSize :: Number -> Number -> Number -> String
badgeSize minSize maxSize size =
  let
    minFontSize = 10.0
    maxFontSize = 24.0
    sizeScaled = (size - minSize) / (maxSize - minSize)  -- in [0; 1] range
    scale' = Math.log (sizeScaled + 1.0) / (Math.log 2.0)  -- in [0; 1] range
    scale = minFontSize + scale' * (maxFontSize - minFontSize)

  in
    show scale <> "px"
614

615

616 617
badges :: SigmaxT.SGraph -> SigmaxT.NodeIds -> Seq.Seq (Record SigmaxT.Node)
badges graph selectedNodeIds = SigmaxT.graphNodes $ SigmaxT.nodesById graph selectedNodeIds
618

619
neighbourBadges :: SigmaxT.SGraph -> SigmaxT.NodeIds -> Seq.Seq (Record SigmaxT.Node)
620 621
neighbourBadges graph selectedNodeIds = SigmaxT.neighbours graph selectedNodes' where
  selectedNodes' = SigmaxT.graphNodes $ SigmaxT.nodesById graph selectedNodeIds
622

arturo's avatar
arturo committed
623 624
---------------------------------------------------------

625
type SendPatches =
626 627
  ( errors       :: T.Box (Array FrontendError)
  , graphId      :: NodeID
628 629
  , metaData     :: GET.MetaData
  , nodes        :: Array (Record SigmaxT.Node)
630
  , reloadForest :: T2.ReloadS
631 632
  , session      :: Session
  , termList     :: TermList
633
  )
634

635
sendPatches :: Record SendPatches -> Effect Unit
636
sendPatches { errors, metaData, nodes, reloadForest, session, termList } = do
637
  launchAff_ do
638
    patches <- (parTraverse (sendPatch termList session metaData) nodes) -- :: Aff (Array CNT.VersionedNgramsPatches)
639 640 641
    let mPatch = last patches
    case mPatch of
      Nothing -> pure unit
642 643
      Just (Left err) -> liftEffect $ do
        T.modify_ (A.cons $ FRESTError { error: err }) errors
arturo's avatar
arturo committed
644
        here.warn2 "[sendPatches] RESTError" err
645
      Just (Right (CNT.Versioned _patch)) -> do
646
        liftEffect $ T2.reload reloadForest
647

648
-- Why is this called delete node?
649 650 651 652
sendPatch :: TermList
          -> Session
          -> GET.MetaData
          -> Record SigmaxT.Node
653
          -> AffRESTError CNT.VersionedNgramsPatches
654
sendPatch termList session (GET.MetaData metaData) node = do
655 656 657 658
    eRet  <- NTC.putNgramsPatches coreParams versioned
    case eRet of
      Left err -> pure $ Left err
      Right ret -> do
659
        _task <- NTC.postNgramsChartsAsync coreParams  -- TODO add task
660
        pure $ Right ret
661
  where
662
    nodeId :: NodeID
663
    nodeId = unsafePartial $ fromJust $ fromString node.id
664

665 666
    versioned :: CNT.VersionedNgramsPatches
    versioned = CNT.Versioned {version: metaData.list.version, data: np}
667

668
    coreParams :: CNT.CoreParams ()
669
    coreParams = {session, nodeId, listIds: [metaData.list.listId], tabType}
670

671 672
    tabNgramType :: CTabNgramType
    tabNgramType = modeTabType node.gargType
673

674 675
    tabType :: TabType
    tabType = TabCorpus (TabNgramType tabNgramType)
676

677
    term :: CNT.NgramsTerm
678
    term = NTC.normNgram tabNgramType node.label
679

680 681
    np :: CNT.NgramsPatches
    np = NTC.singletonPatchMap term $ CNT.NgramsPatch { patch_children: mempty, patch_list }
682

683 684
    patch_list :: CNT.Replace TermList
    patch_list = CNT.Replace { new: termList, old: MapTerm }
685

arturo's avatar
arturo committed
686

687 688 689

------------------------------------------------------------------------

arturo's avatar
arturo committed
690 691
            {-, H.div { className: "col-md-12", id: "horizontal-checkbox" }
              [ H.ul {}
692 693 694 695 696 697 698
                [ checkbox "Pubs"
                , checkbox "Projects"
                , checkbox "Patents"
                , checkbox "Others"
                ]
              ]
              -}
699 700 701 702 703
--------------------------------------------------------------------------

documentation :: Lang -> R.Element
documentation _ =

arturo's avatar
arturo committed
704 705 706 707 708 709 710 711 712
    H.div
    { className: "graph-documentation" }
    [
      H.div
      { className: "graph-documentation__text-section" }
      [
        H.p
        {}
        [
arturo's avatar
arturo committed
713
          B.b_ "What is a graph? "
arturo's avatar
arturo committed
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
        ,
          H.text "Graph is a conveniant tool to explore your documents."
        ]
      ,
        H.p
        {}
        [
          H.text $

            "Nodes are terms selected in your Map List. "
          <>
            "Node size is proportional to the number of documents with the associated term. "
        ]
      ,
        H.p
        {}
        [
          H.text $

            "Edges between nodes represent proximities of terms according to a specific distance between your documents. "
          <>
            "Link strength is proportional to the strenght of terms association."
        ]
      ]
    ,
      H.div
      { className: "graph-documentation__text-section" }
      [
        H.ul
        {}
        [
          H.li
          {}
          [
            H.text $

              "Click on a node to select/unselect and get its information."
          ]
        ,
          H.li
          {}
          [
            H.text $

              "In case of multiple selection, the button unselect clears all selections. "
            <>
              "Use your mouse scroll to zoom in and out in the graph. "
          ]
        ,
          H.li
          {}
          [
            H.text $

              "Use the node filter to create a subgraph with nodes of a given size "
            <>
              "range (e.g. display only generic terms). "
          ]
        ,
          H.li
          {}
          [
            H.text $

              "Use the edge filter so create a subgraph with links in a given range (e.g. keep the strongest association)."
          ]
        ]
      ]
    ]
783 784 785 786 787 788 789 790 791 792

{-
TODO DOC
  Conditional distance between the terms X and Y is the probability to have both terms X and Y in the same textual context.
  Distributional distance between the terms X and Y is the probability to have same others terms in the same textual context as X or Y.

Global/local view:
    The 'change level' button allows to change between global view and node centered view,
    To explore the neighborhood of a selection click on the 'change level' button.
-}