DocList.purs 8.38 KB
Newer Older
arturo's avatar
arturo committed
1
module Gargantext.Components.GraphExplorer.Sidebar.DocList
arturo's avatar
arturo committed
2
  ( docListWrapper
arturo's avatar
arturo committed
3 4 5 6
  ) where

import Gargantext.Prelude

arturo's avatar
arturo committed
7
import Data.Array (concat, head)
arturo's avatar
arturo committed
8
import Data.Foldable (intercalate)
arturo's avatar
arturo committed
9
import Data.Map as Map
arturo's avatar
arturo committed
10 11
import Data.Maybe (Maybe(..))
import Data.Sequence as Seq
arturo's avatar
arturo committed
12
import Data.Set as Set
arturo's avatar
arturo committed
13 14 15 16
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (Variant(..))
arturo's avatar
arturo committed
17 18 19 20 21 22
import Gargantext.Components.FacetsTable (DocumentsView(..), Rows(..), initialPagePath, loadPage, publicationDate)
import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types (CorpusId, DocId, GraphSideDoc(..), ListId)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.RandomText (words)
import Gargantext.Components.Search (SearchQuery(..), SearchType(..))
arturo's avatar
arturo committed
23
import Gargantext.Config (defaultFrontends)
arturo's avatar
arturo committed
24
import Gargantext.Config.REST (RESTError(..))
arturo's avatar
arturo committed
25
import Gargantext.Ends (Frontends, url)
arturo's avatar
arturo committed
26
import Gargantext.Hooks.Loader (useLoaderEffect)
arturo's avatar
arturo committed
27 28
import Gargantext.Hooks.Session (useSession)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
arturo's avatar
arturo committed
29
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
arturo's avatar
arturo committed
30 31
import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, sessionId)
arturo's avatar
arturo committed
32 33
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
arturo's avatar
arturo committed
34
import React.SyntheticEvent as SE
arturo's avatar
arturo committed
35 36 37 38 39 40 41
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T

here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Sidebar.DocList"

arturo's avatar
arturo committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
type Props =
  ( metaData :: GET.MetaData
  )

docListWrapper :: R2.Leaf Props
docListWrapper = R2.leaf docListWrapperCpt

docListWrapperCpt :: R.Component Props
docListWrapperCpt = here.component "wrapper" cpt where
  cpt { metaData: GET.MetaData metaData
      } _ = do
    -- | States
    -- |
    session <- useSession

    { showDoc
    , graph
    , selectedNodeIds
    } <- GraphStore.use

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

    query' /\ query <- R2.useBox' Nothing

    -- | Helpers
    -- |
    let
      nodesMap = SigmaxT.nodesGraphMap graph'

      toSearchQuery ids = SearchQuery
        { expected: SearchDoc
        , query: concat $ toQuery <$> Set.toUnfoldable ids
        }

      toQuery id = case Map.lookup id nodesMap of
        Nothing -> []
        Just n -> words n.label

    -- | Hooks
    -- |
83 84
    R.useEffect1' selectedNodeIds' $ do
      T.write_ (Just $ toSearchQuery selectedNodeIds') query
arturo's avatar
arturo committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

    -- | Render
    -- |
    pure $

      R.fragment
      [
        case (head metaData.corpusId) /\ query' of

          (Just corpusId) /\ (Just q') ->
            docList
            { query: q'
            , session
            , corpusId
            , listId: metaData.list.listId
            , showDoc
arturo's avatar
arturo committed
101
            , frontends: defaultFrontends
arturo's avatar
arturo committed
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
            }

          _ /\ _ ->
            B.caveat
            {}
            [
              H.text "You can link a corpus to retrieve relative documents about your selection"
            ]

      ]

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

type ListProps =
  ( query           :: SearchQuery
  , corpusId        :: CorpusId
  , listId          :: ListId
arturo's avatar
arturo committed
119 120
  , session         :: Session
  , showDoc         :: T.Box (Maybe GraphSideDoc)
arturo's avatar
arturo committed
121
  , frontends       :: Frontends
arturo's avatar
arturo committed
122 123
  )

arturo's avatar
arturo committed
124
docList :: R2.Leaf ListProps
arturo's avatar
arturo committed
125 126
docList = R2.leaf docListCpt

arturo's avatar
arturo committed
127
docListCpt :: R.Component ListProps
arturo's avatar
arturo committed
128 129 130 131 132 133 134 135 136 137 138
docListCpt = here.component "main" cpt where
  -- | Helpers
  -- |
  errorHandler err = do
    here.warn2 "[pageLayout] RESTError" err
    case err of
      ReadJSONError err' ->
        here.warn2 "[pageLayout] ReadJSONError" $ show err'
      _ -> pure unit
  -- | Component
  -- |
arturo's avatar
arturo committed
139
  cpt { query
arturo's avatar
arturo committed
140
      , session
arturo's avatar
arturo committed
141 142
      , corpusId: nodeId
      , listId
arturo's avatar
arturo committed
143
      , showDoc
arturo's avatar
arturo committed
144
      , frontends
arturo's avatar
arturo committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 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 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
      } _ = do
    -- | States
    -- |

    path' /\ path
      <- R2.useBox' $ initialPagePath { nodeId, listId, query, session }

    state' /\ state <-
      R2.useBox' Nothing

    rows' /\ rows <-
      R2.useBox' Nothing

    showDoc' <-
      R2.useLive' showDoc

    -- | Hooks
    -- |

    useLoaderEffect
      { errorHandler
      , state
      , loader: loadPage
      , path: path'
      }

    -- | Effects
    -- |

    -- (on query change, reload fetched docs)
    useUpdateEffect1' query $
      flip T.write_ path $ initialPagePath { nodeId, listId, query, session }

    -- (on fetch success, extract existing docs)
    useUpdateEffect1' state' case state' of
      Nothing -> T.write_ (Just Seq.empty) rows
      Just r -> case r of
        Docs { docs } -> T.write_ (Just docs) rows
        _             -> T.write_ (Just Seq.empty) rows

    -- | Computed
    -- |
    let

      callback :: Maybe GraphSideDoc -> DocId -> Effect Unit
      callback
        Nothing
        new
          = setGraphSideDoc new # Just # flip T.write_ showDoc

      callback
        (Just (GraphSideDoc { docId }))
        new
        | docId == new = T.write_ Nothing showDoc
        | otherwise    = setGraphSideDoc new # Just # flip T.write_ showDoc

      setGraphSideDoc :: DocId -> GraphSideDoc
      setGraphSideDoc docId = GraphSideDoc
        { docId
        , listId
        , corpusId: nodeId
        }

      isSelected :: Maybe GraphSideDoc -> DocumentsView -> Boolean
      isSelected
        (Just (GraphSideDoc { docId }))
        (DocumentsView { id })
          = docId == id

      isSelected
        _
        _
          = false

    -- | Render
    -- |
    pure $

arturo's avatar
arturo committed
223
      R2.fromMaybe rows' \results ->
arturo's avatar
arturo committed
224 225 226

        R.fragment
        [
arturo's avatar
arturo committed
227
          R2.when (results == Seq.empty) $
arturo's avatar
arturo committed
228 229 230 231

            B.caveat
            {}
            [
arturo's avatar
arturo committed
232
              H.text "No document found in your corpus for your selected terms"
arturo's avatar
arturo committed
233 234
            ]
        ,
arturo's avatar
arturo committed
235
          R2.when (not $ eq results Seq.empty) $
arturo's avatar
arturo committed
236 237 238 239 240 241 242 243 244 245

            H.ul
            { className: intercalate " "
                [ "graph-doc-list"
                , "list-group"
                ]
            } $
            Seq.toUnfoldable $ flip Seq.map results \r ->

              item
arturo's avatar
arturo committed
246
              { documentView: (r :: DocumentsView)
arturo's avatar
arturo committed
247 248
              , callback: callback showDoc'
              , isSelected: isSelected showDoc' (r :: DocumentsView)
arturo's avatar
arturo committed
249 250 251 252
              , listId
              , corpusId: nodeId
              , session
              , frontends
arturo's avatar
arturo committed
253 254 255 256 257 258 259 260 261 262
              }
        ]


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

type ItemProps =
  ( documentView :: DocumentsView
  , callback     :: DocId -> Effect Unit
  , isSelected   :: Boolean
arturo's avatar
arturo committed
263 264 265 266
  , corpusId     :: CorpusId
  , listId       :: ListId
  , session      :: Session
  , frontends    :: Frontends
arturo's avatar
arturo committed
267 268 269 270 271 272 273 274 275 276
  )

item :: R2.Leaf ItemProps
item = R2.leaf itemCpt

itemCpt :: R.Component ItemProps
itemCpt = here.component "item" cpt where
  cpt { documentView: dv@(DocumentsView { id, title, source })
      , callback
      , isSelected
arturo's avatar
arturo committed
277 278 279 280
      , listId
      , corpusId
      , session
      , frontends
arturo's avatar
arturo committed
281 282
      } _ = do
    -- Computed
arturo's avatar
arturo committed
283
    let
arturo's avatar
arturo committed
284
      -- Creating a href link
arturo's avatar
arturo committed
285 286 287 288 289 290 291 292 293 294 295 296 297
      route = Routes.CorpusDocument (sessionId session) corpusId listId id
      href = url frontends route

    -- Methods
    let
      onClick ::
           SE.SyntheticMouseEvent
        -> Effect Unit
      onClick event
          = R2.externalOpeningFlag event
        >>= case _ of
              true  -> R.nothing
              false -> SE.preventDefault event *> callback id
arturo's avatar
arturo committed
298 299 300 301

    -- Render
    pure $

arturo's avatar
arturo committed
302
      H.a
arturo's avatar
arturo committed
303 304
      { className: intercalate " "
          [ "graph-doc-list__item"
arturo's avatar
arturo committed
305
          , isSelected ? "graph-doc-list__item--selected" $ ""
arturo's avatar
arturo committed
306
          , "list-group-item"
arturo's avatar
arturo committed
307
          , "text-decoration-none"
arturo's avatar
arturo committed
308
          ]
arturo's avatar
arturo committed
309 310
      , on: { click: onClick }
      , href
arturo's avatar
arturo committed
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
      }
      [
        B.ripple
        { variant: Dark }
        [
          H.div
          { className: "graph-doc-list__item__main" }
          [
            B.div'
            { className: "graph-doc-list__item__title" }
            title
          ,
            B.div'
            { className: "graph-doc-list__item__source" }
            source
          ,
            B.div'
            { className: "graph-doc-list__item__date" } $
            publicationDate dv
          ]
        ,
          H.div
          { className: "graph-doc-list__item__aside" }
          [
            B.icon
            { name: "eye-slash"
            , className: intercalate " "
                [ "text-info"
                , isSelected ? "visible" $ "hidden"
                ]
            }
          ]
        ]
      ]