module Gargantext.Components.GraphExplorer.Toolbar.Controls ( controls ) where import Prelude import Data.Foldable (intercalate) import Data.Maybe (Maybe(..)) import Data.Set as Set import Effect.Timer (setTimeout) import Gargantext.Components.Bootstrap as B import Gargantext.Components.GraphExplorer.Resources as Graph import Gargantext.Components.GraphExplorer.Store as GraphStore import Gargantext.Components.GraphExplorer.Toolbar.Buttons (cameraButton, centerButton, edgesToggleButton, louvainButton, pauseForceAtlasButton, pauseNoverlapButton, multiSelectEnabledButton) import Gargantext.Components.GraphExplorer.Toolbar.RangeControl (edgeConfluenceControl, nodeSizeControl) import Gargantext.Components.GraphExplorer.Toolbar.SlideButton (labelSizeButton, labelRenderedSizeThresholdButton, mouseSelectorSizeSlider) import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Hooks.Sigmax.ForceAtlas2 as ForceAtlas import Gargantext.Hooks.Sigmax.Noverlap as Noverlap import Gargantext.Hooks.Sigmax as Sigmax import Gargantext.Hooks.Sigmax.Sigma as Sigma import Gargantext.Hooks.Sigmax.Types as SigmaxT import Gargantext.Sessions (Session) import Gargantext.Types as GT import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Toestand as T2 import Reactix as R import Reactix.DOM.HTML as H import Toestand as T here :: R2.Here here = R2.here "Gargantext.Components.GraphExplorer.Toolbar.Controls" type Controls = ( fa2Ref :: R.Ref (Maybe ForceAtlas.FA2Layout) , noverlapRef :: R.Ref (Maybe Noverlap.NoverlapLayout) , reloadForest :: T2.ReloadS , session :: Session , sigmaRef :: R.Ref Sigmax.Sigma ) controls :: R2.Leaf Controls controls = R2.leaf controlsCpt controlsCpt :: R.Memo Controls controlsCpt = R.memo' $ here.component "controls" cpt where cpt { fa2Ref , noverlapRef , reloadForest , session , sigmaRef } _ = do -- | States -- | { edgeConfluence , edgeConfluenceRange -- , edgeWeight , forceAtlasState , noverlapState , graph , graphId , graphStage , hyperdataGraph , labelRenderedSizeThreshold , labelSize , mouseSelectorSize , multiSelectEnabled , nodeSize , nodeSizeRange , selectedNodeIds , showEdges , showSidebar , sideTab , transformedGraph } <- GraphStore.use graphId' <- R2.useLive' graphId hyperdataGraph' <- R2.useLive' hyperdataGraph forceAtlasState' <- R2.useLive' forceAtlasState noverlapState' <- R2.useLive' noverlapState graphStage' <- R2.useLive' graphStage selectedNodeIds' <- R2.useLive' selectedNodeIds showSidebar' <- R2.useLive' showSidebar edgeConfluenceRange' <- R2.useLive' edgeConfluenceRange nodeSizeRange' <- R2.useLive' nodeSizeRange -- session <- useSession -- ref to track automatic FA pausing -- If user pauses FA before auto is triggered, clear the timeoutId mFAPauseRef <- R.useRef Nothing -- | Effects -- | -- When graph is changed, cleanup the mFAPauseRef so that forceAtlas -- timeout is retriggered. R.useEffect1' graphStage' $ do case graphStage' of GET.Init -> R.setRef mFAPauseRef Nothing _ -> pure unit -- Handle case when FA is paused from outside events, eg. the automatic timer. R.useEffect' $ Sigmax.handleForceAtlas2Pause fa2Ref forceAtlasState mFAPauseRef Graph.forceAtlas2Settings R.useEffect' do -- here.log2 "[controls] noverlapState'" noverlapState' case R.readRef noverlapRef of Nothing -> pure unit Just noverlap -> do case noverlapState' of SigmaxT.NoverlapRunning -> do Noverlap.start noverlap SigmaxT.NoverlapPaused -> do Noverlap.stop noverlap -- Handle automatic edge hiding when FA is running (to prevent flickering). -- TODO Commented temporarily: this breaks forceatlas rendering after reset -- 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 let renderLabels = SigmaxT.forceAtlasLabelState forceAtlasState' here.log2 "[controls] renderLabels" renderLabels Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Cleanup)] no sigma" $ \sigma -> do Sigma.setSettings sigma { renderLabels } -- v <- T.read showEdges -- here.log2 "[controls] modifed showEdges to forceAtlasState'" v -- Automatic opening of sidebar when a node is selected (but only first time). R.useEffect' $ do if showSidebar' == GT.InitialClosed && (not Set.isEmpty selectedNodeIds') then do T.write_ GT.Opened showSidebar T.write_ GET.SideTabData sideTab else pure unit -- Timer to turn off the initial FA. This is because FA eats up lot of -- CPU, has memory leaks etc. R.useEffect1' forceAtlasState' $ do case forceAtlasState' of SigmaxT.InitialRunning -> do timeoutId <- setTimeout 9000 $ do case forceAtlasState' of SigmaxT.InitialRunning -> T.write_ SigmaxT.Paused forceAtlasState _ -> pure unit R.setRef mFAPauseRef Nothing R.setRef mFAPauseRef $ Just timeoutId pure unit _ -> pure unit let gap = H.span { className: "graph-toolbar__gap" } [] -- | Render -- | pure $ H.nav { className: "graph-toolbar" } [ H.div { className: "flex-shrink-0" } [ H.div { className: "d-flex" } [ -- Actions B.fieldset { className: "graph-toolbar__section" , titleSlot: H.text "Actions" } [ -- resetForceAtlasButton { forceAtlasState, sigmaRef } pauseForceAtlasButton { state: forceAtlasState , title: "Play/pause spatialization" } , pauseNoverlapButton { state: noverlapState , title: "No node overlap" } , gap , cameraButton { id: graphId' , forceAtlasState , hyperdataGraph: hyperdataGraph' , reloadForest , session , sigmaRef: sigmaRef , title: "Save this graph" } ] , -- View Settings B.fieldset { className: "graph-toolbar__section" , titleSlot: H.text "View settings" } [ centerButton { forceAtlasState , sigmaRef , title: "Recenter graph" } , gap , edgesToggleButton { state: showEdges , stateAtlas: forceAtlasState , title: "Show/hide node links" } , gap , louvainButton { forceAtlasState , graph , sigmaRef , transformedGraph , title: "" } ] ] , -- Selection Settings B.fieldset { className: intercalate " " [ "graph-toolbar__section" , "graph-toolbar__section--selection" ] , titleSlot: H.text "Nodes (terms) selection settings" } [ -- zoom: 0 -100 - calculate ratio multiSelectEnabledButton { forceAtlasState , state: multiSelectEnabled } , gap , -- toggle multi node selection -- save button mouseSelectorSizeSlider { forceAtlasState , sigmaRef , state: mouseSelectorSize } ] ] , -- Controls B.fieldset { className: intercalate " " [ "graph-toolbar__section" , "graph-toolbar__section--controls" , "flex-grow-1 flex-shrink-1" ] , titleSlot: H.text "Controls" } [ B.wad [ "d-flex", "gap-6", "px-1" ] [ B.wad [ "d-flex", "flex-column", "flex-grow-1", "pt-1", "gap-4" ] [ edgeConfluenceControl { forceAtlasState , range: edgeConfluenceRange' , state: edgeConfluence } {- , edgeWeightControl { forceAtlasState , range: edgeWeightRange , state: edgeWeight } -} , nodeSizeControl { forceAtlasState , range: nodeSizeRange' , state: nodeSize } ] , B.wad [ "d-flex", "flex-column", "flex-grow-1", "pt-1", "gap-4" ] [ labelSizeButton { forceAtlasState , graph , sigmaRef , state: labelSize } , labelRenderedSizeThresholdButton { forceAtlasState , sigmaRef , state: labelRenderedSizeThreshold } -- , -- nodeSizeControl -- { range: nodeSizeRange -- , state: nodeSize -- } ] ] ] ] -- H.ul {} [ -- change type button (?) -- H.li {} [ centerButton sigmaRef ] -- , H.li {} [ pauseForceAtlasButton {state: forceAtlasState} ] -- , H.li {} [ edgesToggleButton {state: showEdges} ] -- , H.li {} [ louvainToggleButton showLouvain ] -- , H.li {} [ edgeConfluenceControl edgeConfluenceRange edgeConfluence ] -- , H.li {} [ edgeWeightControl edgeWeightRange edgeWeight ] -- -- change level -- -- file upload -- -- run demo -- -- search button -- -- search topics -- , H.li {} [ labelSizeButton sigmaRef localControls.labelSize ] -- labels size: 1-4 -- , H.li {} [ nodeSizeControl nodeSizeRange nodeSize ] -- -- zoom: 0 -100 - calculate ratio -- , H.li {} [ multiSelectEnabledButton multiSelectEnabled ] -- toggle multi node selection -- -- save button -- , H.li {} [ nodeSearchControl { graph: graph -- , multiSelectEnabled: multiSelectEnabled -- , selectedNodeIds: selectedNodeIds } ] -- , H.li {} [ mouseSelectorSizeButton sigmaRef localControls.mouseSelectorSize ] -- , H.li {} [ cameraButton { id: graphId -- , hyperdataGraph: hyperdataGraph -- , session: session -- , sigmaRef: sigmaRef -- , reloadForest: reloadForest } ] -- ] -- ]