module Gargantext.Components.Router (router) where import Gargantext.Prelude import Data.Array (filter, length) import Data.Array as A import Data.Foldable (intercalate) import Data.Maybe (Maybe(..)) import Data.Tuple.Nested ((/\)) import Data.UUID (UUID) import Data.UUID as UUID import Effect (Effect) import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.ErrorsView (errorsView) import Gargantext.Components.Footer (footer) import Gargantext.Components.Forest as Forest import Gargantext.Components.GraphExplorer as GraphExplorer import Gargantext.Components.GraphExplorer.Sidebar as GES import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST import Gargantext.Components.GraphExplorer.TopBar as GETB import Gargantext.Components.Lang (LandingLang(LL_EN)) import Gargantext.Components.Login (login) import Gargantext.Components.Nodes.Annuaire (annuaireLayout) import Gargantext.Components.Nodes.Annuaire.User (userLayout) import Gargantext.Components.Nodes.Annuaire.User.Contact (contactLayout) import Gargantext.Components.Nodes.Corpus (corpusLayout) import Gargantext.Components.Nodes.Corpus.Code (corpusCodeLayout) import Gargantext.Components.Nodes.Corpus.Dashboard (dashboardLayout) import Gargantext.Components.Nodes.Corpus.Document (documentMainLayout) import Gargantext.Components.Nodes.Corpus.Phylo (phyloLayout) import Gargantext.Components.Nodes.File (fileLayout) import Gargantext.Components.Nodes.Frame (frameLayout) import Gargantext.Components.Nodes.Home (homeLayout) import Gargantext.Components.Nodes.Lists as Lists import Gargantext.Components.Nodes.Texts as Texts import Gargantext.Components.Tile (tileBlock) import Gargantext.Components.TopBar as TopBar import Gargantext.Config (defaultFrontends, defaultBackends) import Gargantext.Ends (Backend) import Gargantext.Routes (AppRoute, Tile) import Gargantext.Routes as GR import Gargantext.Sessions (Session, WithSession) import Gargantext.Sessions as Sessions import Gargantext.Types (CorpusId, Handed(..), ListId, NodeID, NodeType(..), SessionId, SidePanelState(..), reverseHanded) import Gargantext.Utils.Reactix as R2 import Reactix as R import Reactix.DOM.HTML as H import Record (get) import Record as Record import Record.Extra as RE import Toestand as T import Type.Proxy (Proxy(..)) here :: R2.Here here = R2.here "Gargantext.Components.Router" type Props = ( boxes :: Boxes ) type SessionProps = ( sessionId :: SessionId | Props ) type SessionNodeProps = ( nodeId :: NodeID | SessionProps ) type Props' = ( backend :: Backend, route' :: AppRoute | Props ) router :: R2.Leaf Props router = R2.leafComponent routerCpt routerCpt :: R.Component Props routerCpt = here.component "router" cpt where cpt { boxes: boxes@{ handed } } _ = do handed' <- T.useLive T.unequal handed let handedClassName = case handed' of LeftHanded -> "left-handed" RightHanded -> "right-handed" pure $ R.fragment ([ loginModal { boxes } , topBar { boxes } , errorsView { errors: boxes.errors } [] , H.div { className: handedClassName <> " router-inner" } $ reverseHanded handed' $ [ forest { boxes } , mainPage { boxes } , sidePanel { boxes } ] ]) loginModal :: R2.Leaf Props loginModal = R2.leafComponent loginModalCpt loginModalCpt :: R.Component Props loginModalCpt = here.component "loginModal" cpt where cpt { boxes: boxes@{ showLogin } } _ = do showLogin' <- T.useLive T.unequal showLogin pure $ if showLogin' then login' boxes else H.div {} [] topBar :: R2.Leaf Props topBar = R2.leafComponent topBarCpt topBarCpt :: R.Component Props topBarCpt = here.component "topBar" cpt where cpt { boxes: boxes@{ route } } _ = do route' <- T.useLive T.unequal route let children = case route' of GR.PGraphExplorer _s _g -> [ GETB.topBar { boxes } ] _ -> [] pure $ TopBar.topBar { boxes } children mainPage :: R2.Leaf Props mainPage = R2.leafComponent mainPageCpt mainPageCpt :: R.Component Props mainPageCpt = here.component "mainPage" cpt where cpt { boxes } _ = do -- States route <- R2.useLive' boxes.route tileAxisXList <- R2.useLive' boxes.tileAxisXList tileAxisYList <- R2.useLive' boxes.tileAxisYList -- Computed let findTile :: UUID -> Record Tile -> Boolean findTile id tile = eq id $ get (Proxy :: Proxy "id") tile deleteTile :: Record Tile -> T.Box (Array (Record Tile)) -> (Unit -> Effect Unit) deleteTile tile listBox = const do list <- T.read listBox newList <- pure $ filter (_ # tile.id # findTile # not) list T.write_ newList listBox let hasHorizontalTiles = not $ eq 0 $ length tileAxisXList let hasVerticalTiles = not $ eq 0 $ length tileAxisYList -- Render pure $ H.div { className: "main-page" } [ H.div { className: intercalate " " [ "main-page__main-row" , if (hasVerticalTiles) then "main-page__main-row--with-y-tiles" else "" , if (hasVerticalTiles && not hasHorizontalTiles) then "main-page__main-row--only-y-tiles" else "" ] } [ -- main render route H.div { className: "main-page__main-route" } [ renderRoute { boxes, route } ] , -- optional tile render route [Y Axis ~ aside vertical column] case tileAxisYList of [] -> mempty _ -> H.div { className: intercalate " " [ "main-page__vertical-tiles" , "main-page__vertical-tiles--" <> (show $ length tileAxisYList) ] } $ tileAxisYList <#> \tile -> tileBlock { boxes , tile , key: UUID.toString tile.id , closeCallback: deleteTile tile boxes.tileAxisYList } [ renderRoute { boxes, route: tile.route } ] ] , -- optional tile render route [X Axis ~ bottom horizontal row] case tileAxisXList of [] -> mempty _ -> H.div { className: intercalate " " [ "main-page__horizontal-tiles" , "main-page__horizontal-tiles--" <> (show $ length tileAxisXList) ] } $ tileAxisXList <#> \tile -> tileBlock { boxes , tile , key: UUID.toString tile.id , closeCallback: deleteTile tile boxes.tileAxisXList } [ renderRoute { boxes, route: tile.route } ] ] forest :: R2.Leaf Props forest = R2.leafComponent forestCpt forestCpt :: R.Component Props forestCpt = here.component "forest" cpt where cpt { boxes: boxes@{ showTree } } _ = do showTree' <- T.useLive T.unequal showTree pure $ if not showTree' then mempty else Forest.forestLayout { boxes , frontends: defaultFrontends } [] sidePanel :: R2.Leaf Props sidePanel = R2.leafComponent sidePanelCpt sidePanelCpt :: R.Component Props sidePanelCpt = here.component "sidePanel" cpt where cpt props@{ boxes: { session , sidePanelState } } _ = do session' <- T.useLive T.unequal session sidePanelState' <- T.useLive T.unequal sidePanelState case session' of Nothing -> pure $ H.div {} [] Just s -> case sidePanelState' of Opened -> pure $ openedSidePanel (Record.merge { session: s } props) [] _ -> pure $ H.div {} [] type RenderRouteProps = ( route :: AppRoute | Props ) renderRoute :: R2.Leaf RenderRouteProps renderRoute = R2.leafComponent renderRouteCpt renderRouteCpt :: R.Component RenderRouteProps renderRouteCpt = here.component "renderRoute" cpt where cpt { boxes, route } _ = do let sessionNodeProps sId nId = { nodeId: nId , sessionId: sId , boxes } pure $ R.fragment [ case route of GR.Annuaire s n -> annuaire (sessionNodeProps s n) [] GR.ContactPage s a n -> contact (Record.merge { annuaireId: a } $ sessionNodeProps s n) [] GR.Corpus s n -> corpus (sessionNodeProps s n) [] GR.CorpusCode s n -> corpusCode (sessionNodeProps s n) [] GR.CorpusDocument s c l n -> corpusDocument (Record.merge { corpusId: c, listId: l } $ sessionNodeProps s n) [] GR.Dashboard s n -> dashboard (sessionNodeProps s n) [] GR.Document s l n -> document (Record.merge { listId: l } $ sessionNodeProps s n) [] GR.Folder s n -> corpus (sessionNodeProps s n) [] GR.FolderPrivate s n -> corpus (sessionNodeProps s n) [] GR.FolderPublic s n -> corpus (sessionNodeProps s n) [] GR.FolderShared s n -> corpus (sessionNodeProps s n) [] GR.Home -> home { boxes } [] GR.Lists s n -> lists (sessionNodeProps s n) [] GR.Login -> login' boxes GR.PGraphExplorer s g -> graphExplorer (sessionNodeProps s g) [] GR.PhyloExplorer s g -> phyloExplorer (sessionNodeProps s g) [] GR.RouteFile s n -> routeFile (sessionNodeProps s n) [] GR.RouteFrameWrite s n -> routeFrame (Record.merge { nodeType: NodeFrameWrite } $ sessionNodeProps s n) [] GR.RouteFrameCalc s n -> routeFrame (Record.merge { nodeType: NodeFrameCalc } $ sessionNodeProps s n) [] GR.RouteFrameCode s n -> routeFrame (Record.merge { nodeType: NodeFrameNotebook } $ sessionNodeProps s n) [] GR.RouteFrameVisio s n -> routeFrame (Record.merge { nodeType: NodeFrameVisio } $ sessionNodeProps s n) [] GR.Team s n -> team (sessionNodeProps s n) [] GR.NodeTexts s n -> texts (sessionNodeProps s n) [] GR.UserPage s n -> user (sessionNodeProps s n) [] ] type AuthedProps = ( content :: Session -> R.Element | SessionProps ) authed :: R2.Component AuthedProps authed = R.createElement authedCpt authedCpt :: R.Component AuthedProps authedCpt = here.component "authed" cpt where cpt props@{ boxes: { session, sessions } , content , sessionId } _ = do sessions' <- T.useLive T.unequal sessions let session' = Sessions.lookup sessionId sessions' R.useEffect' $ do T.write_ session' session case session' of Nothing -> pure $ home homeProps [] Just s -> pure $ R.fragment [ content s, footer {} [] ] where homeProps = RE.pick props :: Record Props openedSidePanel :: R2.Component (WithSession Props) openedSidePanel = R.createElement openedSidePanelCpt openedSidePanelCpt :: R.Component (WithSession Props) openedSidePanelCpt = here.component "openedSidePanel" cpt where cpt { boxes: boxes@{ route , sidePanelGraph , sidePanelState , sidePanelTexts } , session } _ = do { mGraph, mMetaData } <- GEST.focusedSidePanel sidePanelGraph mGraph' <- T.useLive T.unequal mGraph mGraphMetaData' <- T.useLive T.unequal mMetaData route' <- T.useLive T.unequal route let wrapper = H.div { className: "side-panel" } case route' of GR.Lists _s _n -> do pure $ wrapper [ Lists.sidePanel { session , sidePanelState } [] ] GR.PGraphExplorer _s g -> do case (mGraph' /\ mGraphMetaData') of (Nothing /\ _) -> pure $ wrapper [] (_ /\ Nothing) -> pure $ wrapper [] (Just graph /\ Just metaData) -> do pure $ wrapper [ GES.sidebar { boxes , frontends: defaultFrontends , graph , graphId: g , metaData , session } [] ] GR.NodeTexts _s _n -> do pure $ wrapper [ Texts.textsSidePanel { boxes , session , sidePanel: sidePanelTexts } [] ] _ -> pure $ wrapper [] annuaire :: R2.Component SessionNodeProps annuaire = R.createElement annuaireCpt annuaireCpt :: R.Component SessionNodeProps annuaireCpt = here.component "annuaire" cpt where cpt props@{ nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> annuaireLayout { frontends: defaultFrontends , nodeId , session } } sessionProps) [] corpus :: R2.Component SessionNodeProps corpus = R.createElement corpusCpt corpusCpt :: R.Component SessionNodeProps corpusCpt = here.component "corpus" cpt where cpt props@{ boxes, nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> corpusLayout { boxes , nodeId , session } } sessionProps) [] corpusCode :: R2.Component SessionNodeProps corpusCode = R.createElement corpusCodeCpt corpusCodeCpt :: R.Component SessionNodeProps corpusCodeCpt = here.component "corpusCode" cpt where cpt props@{ boxes, nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps authedProps = Record.merge { content: \session -> corpusCodeLayout { nodeId , session , boxes } } sessionProps pure $ authed authedProps [] type CorpusDocumentProps = ( corpusId :: CorpusId , listId :: ListId | SessionNodeProps ) corpusDocument :: R2.Component CorpusDocumentProps corpusDocument = R.createElement corpusDocumentCpt corpusDocumentCpt :: R.Component CorpusDocumentProps corpusDocumentCpt = here.component "corpusDocument" cpt where cpt props@{ corpusId: corpusId', listId, nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> documentMainLayout { mCorpusId: Just corpusId' , listId: listId , nodeId , session } [] } sessionProps )[] dashboard :: R2.Component SessionNodeProps dashboard = R.createElement dashboardCpt dashboardCpt :: R.Component SessionNodeProps dashboardCpt = here.component "dashboard" cpt where cpt props@{ boxes, nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> dashboardLayout { boxes, nodeId, session } [] } sessionProps) [] type DocumentProps = ( listId :: ListId | SessionNodeProps ) document :: R2.Component DocumentProps document = R.createElement documentCpt documentCpt :: R.Component DocumentProps documentCpt = here.component "document" cpt where cpt props@{ listId, nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> documentMainLayout { listId , nodeId , mCorpusId: Nothing , session } [] } sessionProps) [] graphExplorer :: R2.Component SessionNodeProps graphExplorer = R.createElement graphExplorerCpt graphExplorerCpt :: R.Component SessionNodeProps graphExplorerCpt = here.component "graphExplorer" cpt where cpt props@{ boxes , nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> GraphExplorer.explorerLayoutWithKey { boxes , graphId: nodeId , key: "graphId-" <> show nodeId , session } [] } sessionProps) [] -- GraphExplorer.explorerLayout { boxes -- , graphId: nodeId -- , session } [] } sessionProps) [] phyloExplorer :: R2.Component SessionNodeProps phyloExplorer = R.createElement phyloExplorerCpt phyloExplorerCpt :: R.Component SessionNodeProps phyloExplorerCpt = here.component "phylo" cpt where cpt props@{ nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> phyloLayout { nodeId, session } [] } sessionProps) [] home :: R2.Component Props home = R.createElement homeCpt homeCpt :: R.Component Props homeCpt = here.component "home" cpt where cpt { boxes } _ = do pure $ homeLayout { boxes } lists :: R2.Component SessionNodeProps lists = R.createElement listsCpt listsCpt :: R.Component SessionNodeProps listsCpt = here.component "lists" cpt where cpt props@{ boxes , nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> Lists.listsLayout { boxes , nodeId , session , sessionUpdate: \_ -> pure unit } [] } sessionProps) [] login' :: Boxes -> R.Element login' { backend, sessions, showLogin: visible } = login { backend , backends: A.fromFoldable defaultBackends , sessions , visible } routeFile :: R2.Component SessionNodeProps routeFile = R.createElement routeFileCpt routeFileCpt :: R.Component SessionNodeProps routeFileCpt = here.component "routeFile" cpt where cpt props@{ nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> fileLayout { nodeId, session } } sessionProps) [] type RouteFrameProps = ( nodeType :: NodeType | SessionNodeProps ) routeFrame :: R2.Component RouteFrameProps routeFrame = R.createElement routeFrameCpt routeFrameCpt :: R.Component RouteFrameProps routeFrameCpt = here.component "routeFrame" cpt where cpt props@{ nodeId, nodeType } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> frameLayout { nodeId, nodeType, session } } sessionProps) [] team :: R2.Component SessionNodeProps team = R.createElement teamCpt teamCpt :: R.Component SessionNodeProps teamCpt = here.component "team" cpt where cpt props@{ boxes, nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> corpusLayout { boxes , nodeId , session } } sessionProps) [] texts :: R2.Component SessionNodeProps texts = R.createElement textsCpt textsCpt :: R.Component SessionNodeProps textsCpt = here.component "texts" cpt where cpt props@{ boxes , nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> Texts.textsLayout { boxes , frontends: defaultFrontends , nodeId , session } [] } sessionProps) [] user :: R2.Component SessionNodeProps user = R.createElement userCpt userCpt :: R.Component SessionNodeProps userCpt = here.component "user" cpt where cpt props@{ boxes , nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps pure $ authed (Record.merge { content: \session -> userLayout { boxes , frontends: defaultFrontends , nodeId , session } [] } sessionProps) [] type ContactProps = ( annuaireId :: NodeID | SessionNodeProps ) contact :: R2.Component ContactProps contact = R.createElement contactCpt contactCpt :: R.Component ContactProps contactCpt = here.component "contact" cpt where cpt props@{ annuaireId , boxes , nodeId } _ = do let sessionProps = RE.pick props :: Record SessionProps -- let forestedProps = RE.pick props :: Record Props pure $ authed (Record.merge { content: \session -> contactLayout { annuaireId , boxes , frontends: defaultFrontends , nodeId , session } [] } sessionProps) []