Tree.purs 7.02 KB
Newer Older
1 2 3
module Gargantext.Components.Forest.Tree where

import DOM.Simple.Console (log2)
4
import Data.Array as A
5
import Data.Maybe (Maybe)
6 7
import Data.Set as Set
import Data.Tuple (Tuple(..), fst, snd)
8
import Data.Tuple.Nested ((/\))
9
import Effect.Aff (Aff)
10
import Effect.Class (liftEffect)
11
import Gargantext.Components.Forest.Tree.Node.Action (Action(..), CreateValue(..), FTree, ID, LNode(..), NTree(..), Reload, RenameValue(..), Tree, createNode, deleteNode, loadNode, renameNode)
12 13
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadFile)
import Gargantext.Components.Forest.Tree.Node.Box (nodeMainSpan)
14
import Gargantext.Ends (Frontends)
15
import Gargantext.Hooks.Loader (useLoader)
16
import Gargantext.Prelude (Unit, bind, const, discard, map, pure, void, ($), (+), (/=), (<>))
17
import Gargantext.Routes (AppRoute)
18
import Gargantext.Sessions (OpenNodes, Session, mkNodeId)
19
import Gargantext.Types as GT
20 21
import Reactix as R
import Reactix.DOM.HTML as H
22

23 24 25 26 27 28 29 30 31
type CommonProps =
  (
      frontends :: Frontends
    , mCurrentRoute :: Maybe AppRoute
    , openNodes :: R.State OpenNodes
    , reload :: R.State Reload
    , session :: Session
  )

32
------------------------------------------------------------------------
33
type Props = ( root          :: ID
34
             | CommonProps
35
             )
36

37 38 39 40 41 42
treeView :: Record Props -> R.Element
treeView props = R.createElement treeViewCpt props []

treeViewCpt :: R.Component Props
treeViewCpt = R.hooksComponent "G.C.Tree.treeView" cpt
  where
43
    cpt { root, mCurrentRoute, session, frontends, openNodes, reload } _children = do
44 45
      pure $ treeLoadView
        { root, mCurrentRoute, session, frontends, openNodes, reload }
46

47
treeLoadView :: Record Props -> R.Element
48 49
treeLoadView p = R.createElement treeLoadView' p []

50
treeLoadView' :: R.Component Props
51
treeLoadView' = R.hooksComponent "TreeLoadView" cpt
52
  where
53
    cpt {root, mCurrentRoute, session, frontends, openNodes, reload} _ = do
54 55 56
      let fetch _ = loadNode session root
      let paint loaded = loadedTreeView {tree: loaded, mCurrentRoute, session, frontends, openNodes, reload}
      useLoader {root, counter: fst reload} fetch paint
57

58
type TreeViewProps = ( tree          :: FTree
59
                     | CommonProps
60 61
                     )

62 63 64 65 66 67

loadedTreeView :: Record TreeViewProps -> R.Element
loadedTreeView p = R.createElement loadedTreeView' p []

loadedTreeView' :: R.Component TreeViewProps
loadedTreeView' = R.hooksComponent "LoadedTreeView" cpt
68
  where
69
    cpt {tree, mCurrentRoute, session, frontends, openNodes, reload} _ = do
70
      tasks <- R.useState' []
71 72

      pure $ H.div {className: "tree"}
73
        [ toHtml { frontends, mCurrentRoute, openNodes, reload, session, tasks, tree } ]
74

75
------------------------------------------------------------------------
76 77 78

type ToHtmlProps =
  (
79
    tasks :: R.State (Array GT.AsyncTaskWithType)
80
  , tree :: FTree
81
  | CommonProps
82 83 84 85 86 87 88 89 90 91
  )

toHtml :: Record ToHtmlProps -> R.Element
toHtml { frontends
       , mCurrentRoute
       , openNodes
       , reload: reload@(_ /\ setReload)
       , session
       , tasks: tasks@(asyncTasks /\ setAsyncTasks)
       , tree: tree@(NTree (LNode {id, name, nodeType}) ary) } = R.createElement el {} []
92 93
  where
    el = R.hooksComponent "NodeView" cpt
94
    pAction = performAction {openNodes, reload, session, tasks, tree}
95

96
    cpt props _ = do
97 98
      let nodeId = mkNodeId session id
      let folderIsOpen = Set.member nodeId (fst openNodes)
99
      let setFn = if folderIsOpen then Set.delete else Set.insert
100
      let toggleFolderIsOpen _ = (snd openNodes) (setFn nodeId)
101
      let folderOpen = Tuple folderIsOpen toggleFolderIsOpen
102 103 104 105 106

      let withId (NTree (LNode {id: id'}) _) = id'

      pure $ H.ul {}
        [ H.li {}
107 108 109 110 111 112 113 114 115 116 117
          ( [ nodeMainSpan { id
                           , asyncTasks
                           , dispatch: pAction
                           , folderOpen
                           , frontends
                           , mCurrentRoute
                           , name
                           , nodeType
                           , onAsyncTaskFinish
                           , session
                           } ]
118
            <> childNodes {children: ary, folderOpen, frontends, mCurrentRoute, openNodes, reload, session }
119 120 121
          )
        ]

122 123 124
    onAsyncTaskFinish (GT.AsyncTaskWithType {task: GT.AsyncTask {id: id'}}) = do
      setAsyncTasks $ const newAsyncTasks
      setReload (_ + 1)
125
      where
126
        newAsyncTasks = A.filter (\(GT.AsyncTaskWithType {task: GT.AsyncTask {id: id''}}) -> id' /= id'') asyncTasks
127

128

129 130 131 132 133 134 135 136 137 138 139 140 141
type ChildNodesProps =
  (
      children :: Array FTree
    , folderOpen :: R.State Boolean
    | CommonProps
  )


childNodes :: Record ChildNodesProps -> Array R.Element
childNodes { children: [] } = []
childNodes { folderOpen: (false /\ _) } = []
childNodes { children, folderOpen: (true /\ _), frontends, mCurrentRoute, openNodes, reload, session } =
  map (\ctree -> childNode {tree: ctree, asyncTasks: []}) $ sorted children
142
    where
143
      sorted :: Array FTree -> Array FTree
144
      sorted = A.sortWith (\(NTree (LNode {id}) _) -> id)
145 146 147
      childNode :: Tree -> R.Element
      childNode props = R.createElement el props []
      el = R.hooksComponent "ChildNodeView" cpt
148
      cpt {tree, asyncTasks} _ = do
149
        tasks <- R.useState' asyncTasks
150
        pure $ toHtml { frontends, mCurrentRoute, openNodes, reload, session, tasks, tree }
151

152
performAction :: { openNodes :: R.State OpenNodes
153
                 , reload :: R.State Reload
154 155 156
                 , session :: Session
                 , tasks :: R.State (Array GT.AsyncTaskWithType)
                 , tree :: FTree }
157 158
              -> Action
              -> Aff Unit
159 160 161 162
performAction { openNodes: (_ /\ setOpenNodes)
              , reload: (_ /\ setReload)
              , session
              , tree: (NTree (LNode {id}) _) } DeleteNode = do
163
  void $ deleteNode session id
164
  liftEffect do
165
    setOpenNodes (Set.delete (mkNodeId session id))
166
    setReload (_ + 1)
167

168 169 170 171
performAction { reload: (_ /\ setReload)
              , session
              , tasks: (_ /\ setAsyncTasks)
              , tree: (NTree (LNode {id}) _) } (SearchQuery task) = do
172
  liftEffect $ setAsyncTasks $ A.cons task
173
  liftEffect $ log2 "[performAction] SearchQuery task:" task
Alexandre Delanoë's avatar
Alexandre Delanoë committed
174
  liftEffect $ setReload (_ + 1)
175

176 177 178
performAction { reload: (_ /\ setReload)
              , session
              , tree: (NTree (LNode {id}) _) } (Submit name)  = do
179
  void $ renameNode session id $ RenameValue {name}
180 181
  liftEffect do
    setReload (_ + 1)
182

183 184 185 186
performAction { openNodes: (_ /\ setOpenNodes)
              , reload: (_ /\ setReload)
              , session
              , tree: (NTree (LNode {id}) _) } (CreateSubmit name nodeType) = do
187
  void $ createNode session id $ CreateValue {name, nodeType}
188
  liftEffect do
189
    setOpenNodes (Set.insert (mkNodeId session id))
190
    setReload (_ + 1)
191

192 193 194
performAction { session
              , tasks: (_ /\ setAsyncTasks)
              , tree: (NTree (LNode {id}) _) } (UploadFile nodeType fileType mName contents) = do
195
  task <- uploadFile session nodeType id fileType {mName, contents}
196
  liftEffect $ setAsyncTasks $ A.cons task
197
  liftEffect $ log2 "uploaded, task:" task