Commit a554c2bd authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into dev-graphql

parents 6d2a6fc3 0b5b7644
Pipeline #1981 failed with stage
{
"name": "Gargantext",
"version": "0.0.4.3",
"version": "0.0.4.4",
"scripts": {
"generate-purs-packages-nix": "./nix/generate-purs-packages.nix",
"generate-psc-packages-nix": "./nix/generate-packages-json.bash",
......
......@@ -18,6 +18,7 @@ to generate this file without the comments in this block.
, "argonaut"
, "argonaut-codecs"
, "argonaut-core"
, "arraybuffer-types"
, "arrays"
, "bifunctors"
, "colors"
......
'use strict';
exports.back = function() {
return function() {
history.back();
}
}
exports.link = function (url) {
return function() {
window.location.href = url
}
}
\ No newline at end of file
......@@ -21,15 +21,15 @@ import Gargantext.Components.Forest.Tree.Node.Action.Share as Share
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Update (updateRequest)
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryFile, uploadFile)
import Gargantext.Components.Forest.Tree.Node.Action.WriteNodesDocuments (documentsFromWriteNodesReq)
import Gargantext.Components.Forest.Tree.Node.Box (nodePopupView)
import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..), ID, fTreeID)
import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..), fTreeID)
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(..))
import Gargantext.Config.REST (RESTError, logRESTError)
import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Hooks.LinkHandler (Methods, useLinkHandler)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Prelude (Ordering, Unit, bind, compare, discard, pure, unit, void, ($), (<$>), (<>))
import Gargantext.Routes (AppRoute(Home), SessionRoute(..), appPath, nodeTypeAppRoute)
import Gargantext.Routes (AppRoute(Home), SessionRoute(..), nodeTypeAppRoute)
import Gargantext.Sessions (Session, get, sessionId)
import Gargantext.Types (NodeType(..))
import Gargantext.Types as GT
......@@ -41,9 +41,6 @@ import Reactix.DOM.HTML as H
import Record as Record
import Toestand as T
foreign import back :: Effect Unit
foreign import link :: String -> Effect Unit
here :: R2.Here
here = R2.here "Gargantext.Components.FolderView"
......@@ -98,9 +95,10 @@ folderViewMainCpt = here.component "folderViewMainCpt" cpt where
, reload
, session
, setPopoverRef } _ = do
linkHandlers <- useLinkHandler
let foldersS = A.sortBy sortFolders folders
let backHome = isBackHome nodeType
let parent = makeParentFolder parentId session backFolder backHome
let parent = makeParentFolder linkHandlers parentId session backFolder backHome
let children = makeFolderElements foldersS { boxes, nodeId, reload, session, setPopoverRef }
pure $ H.div {className: "fv folders"} $ parent <> children
......@@ -117,18 +115,18 @@ folderViewMainCpt = here.component "folderViewMainCpt" cpt where
, style: FolderChild
, text: node.name } []
makeParentFolder :: Maybe Int -> Session -> Boolean -> Boolean -> Array R.Element
makeParentFolder (Just parentId) session _ _ =
makeParentFolder :: Record Methods -> Maybe Int -> Session -> Boolean -> Boolean -> Array R.Element
makeParentFolder _ (Just parentId) session _ _ =
-- FIXME: The NodeType here should not be hardcoded to FolderPrivate but we currently can't get the actual NodeType
-- without performing another API call. Also parentId is never being returned by this API even when it clearly exists
[ folderSimple {style: FolderUp, text: "..", nodeId: parentId, nodeType: GT.FolderPrivate, session: session} [] ]
makeParentFolder Nothing _ _ true = [ H.a {className: "btn btn-primary", href: appPath Home} [ H.i { className: "fa fa-folder-open" } []
makeParentFolder linkHandlers Nothing _ _ true = [ H.button {className: "btn btn-primary", on: { click: \_ -> linkHandlers.goToRoute Home}} [ H.i { className: "fa fa-folder-open" } []
, H.br {}
, H.text ".."] ]
makeParentFolder Nothing _ true _ = [ H.button {className: "btn btn-primary", on: { click: back } } [ H.i { className: "fa fa-folder-open" } []
makeParentFolder linkHandlers Nothing _ true _ = [ H.button {className: "btn btn-primary", on: { click: \_ -> linkHandlers.goToPreviousPage } } [ H.i { className: "fa fa-folder-open" } []
, H.br {}
, H.text ".."] ]
makeParentFolder Nothing _ _ _ = []
makeParentFolder _ Nothing _ _ _ = []
sortFolders :: FTree -> FTree -> Ordering
......@@ -156,9 +154,10 @@ folderSimple = R.createElement folderSimpleCpt
folderSimpleCpt :: R.Component FolderSimpleProps
folderSimpleCpt = here.component "folderSimpleCpt" cpt where
cpt {style, text, nodeId, session, nodeType} _ = do
{ goToRoute } <- useLinkHandler
let sid = sessionId session
pure $ H.a { className: "btn btn-primary"
, href: "/#/" <> getFolderPath nodeType sid nodeId }
pure $ H.button { className: "btn btn-primary"
, on: {click: \_ -> goToRoute $ getFolderPath nodeType sid nodeId} }
[ H.i { className: icon style nodeType } []
, H.br {}
, H.text text ]
......@@ -167,8 +166,8 @@ folderSimpleCpt = here.component "folderSimpleCpt" cpt where
icon FolderUp _ = "fa fa-folder-open"
icon _ nodeType = GT.fldr nodeType false
getFolderPath :: GT.NodeType -> GT.SessionId -> Int -> String
getFolderPath nodeType sid nodeId = appPath $ fromMaybe Home $ nodeTypeAppRoute nodeType sid nodeId
getFolderPath :: GT.NodeType -> GT.SessionId -> Int -> AppRoute
getFolderPath nodeType sid nodeId = fromMaybe Home $ nodeTypeAppRoute nodeType sid nodeId
type FolderProps =
( boxes :: Boxes
......@@ -194,6 +193,7 @@ folderCpt = here.component "folderCpt" cpt where
let sid = sessionId session
let dispatch a = performAction a { boxes, nodeId, parentId, reload, session, setPopoverRef }
popoverRef <- R.useRef null
{ goToRoute } <- useLinkHandler
R.useEffect' $ do
R.setRef setPopoverRef $ Just $ Popover.setOpen popoverRef
......@@ -210,7 +210,7 @@ folderCpt = here.component "folderCpt" cpt where
popOverIcon
, mNodePopupView (Record.merge props { dispatch }) (onPopoverClose popoverRef)
]]
, H.button {on: {click: link ("/#/" <> getFolderPath nodeType sid nodeId) }, className: "btn btn-primary fv btn" } [
, H.button {on: {click: \_ -> goToRoute $ getFolderPath nodeType sid nodeId }, className: "btn btn-primary fv btn" } [
H.i {className: icon style nodeType} []
, H.br {}
, H.text text]]
......@@ -220,8 +220,8 @@ folderCpt = here.component "folderCpt" cpt where
icon FolderUp _ = "fa fa-folder-open"
icon _ nodeType = GT.fldr nodeType false
getFolderPath :: GT.NodeType -> GT.SessionId -> Int -> String
getFolderPath nodeType sid nodeId = appPath $ fromMaybe Home $ nodeTypeAppRoute nodeType sid nodeId
getFolderPath :: GT.NodeType -> GT.SessionId -> Int -> AppRoute
getFolderPath nodeType sid nodeId = fromMaybe Home $ nodeTypeAppRoute nodeType sid nodeId
onPopoverClose popoverRef _ = Popover.setOpen popoverRef false
......@@ -240,14 +240,20 @@ folderCpt = here.component "folderCpt" cpt where
, session: props.session
}
backButton :: R.Element
backButton =
H.button {
className: "btn btn-primary"
, on: {click: back}
} [
H.i { className: "fa fa-arrow-left", title: "Previous view"} []
]
backButton :: R2.Component ()
backButton = R.createElement backButtonCpt
backButtonCpt :: R.Component ()
backButtonCpt = R.hooksComponent "backButton" cpt where
cpt _ _ = do
{ goToPreviousPage } <- useLinkHandler
pure $
H.button {
className: "btn btn-primary"
, on: { click: \_ -> goToPreviousPage }
} [
H.i { className: "fa fa-arrow-left", title: "Previous view"} []
]
type LoadProps =
(
......
......@@ -17,7 +17,7 @@ import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect)
import Gargantext.Components.Forest.Tree.Node.Action (Props)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..), readUFBAsText)
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..), readUFBAsBase64, readUFBAsText)
import Gargantext.Components.Forest.Tree.Node.Tools (fragmentPT, formChoiceSafe, panel)
import Gargantext.Components.Lang (Lang(..))
import Gargantext.Components.ListSelection as ListSelection
......@@ -193,7 +193,6 @@ uploadButtonCpt = here.component "uploadButton" cpt
let disabled = isNothing mFile' || onPending
pure $
H.div
{ className: "action-upload-button" }
[
......@@ -232,6 +231,11 @@ uploadButtonCpt = here.component "uploadButton" cpt
case fileType' of
Arbitrary ->
dispatch $ UploadArbitraryFile (Just name) blob selection'
ZIP -> do
liftEffect $ here.log "[uploadButton] reading base64"
contents <- readUFBAsBase64 blob
liftEffect $ here.log "[uploadButton] base64 read"
dispatch $ UploadFile nodeType fileType' (Just name) contents selection'
_ -> do
contents <- readUFBAsText blob
dispatch $ UploadFile nodeType fileType' (Just name) contents selection'
......@@ -393,14 +397,14 @@ uploadArbitraryFile :: Session
-> Aff (Either RESTError GT.AsyncTaskWithType)
uploadArbitraryFile session id {mName, blob: UploadFileBlob blob} selection = do
contents <- readAsDataURL blob
uploadArbitraryDataURL session id mName contents
uploadArbitraryDataURL :: Session
-> ID
-> Maybe String
-> String
-> Aff (Either RESTError GT.AsyncTaskWithType)
uploadArbitraryDataURL session id mName contents' = do
uploadArbitraryData session id mName contents
uploadArbitraryData :: Session
-> ID
-> Maybe String
-> String
-> Aff (Either RESTError GT.AsyncTaskWithType)
uploadArbitraryData session id mName contents' = do
let re = fromRight' (\_ -> unsafeCrashWith "Unexpected Left") $ DSR.regex "data:.*;base64," DSRF.noFlags
contents = DSR.replace re "" contents'
eTask :: Either RESTError GT.AsyncTask <- postWwwUrlencoded session p (bodyParams contents)
......
module Gargantext.Components.Forest.Tree.Node.Action.Upload.Types where
import Data.Generic.Rep (class Generic)
import Gargantext.Prelude
import Data.ArrayBuffer.Types (ArrayBuffer)
import Data.Eq.Generic (genericEq)
import Data.Show.Generic (genericShow)
import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..))
import Data.Show.Generic (genericShow)
import Effect.Aff (Aff)
import Gargantext.Utils.ArrayBuffer (arrayBufferToBase64)
import Web.File.Blob (Blob, size)
import Web.File.FileReader.Aff (readAsText)
import Gargantext.Prelude
import Web.File.FileReader.Aff (readAsArrayBuffer, readAsText)
data FileType = CSV | CSV_HAL | WOS | PresseRIS | Arbitrary | JSON | ZIP
......@@ -33,5 +35,16 @@ derive instance Generic UploadFileBlob _
instance Eq UploadFileBlob where
eq (UploadFileBlob b1) (UploadFileBlob b2) = eq (size b1) (size b2)
readUFBAsArrayBuffer :: UploadFileBlob -> Aff ArrayBuffer
readUFBAsArrayBuffer (UploadFileBlob b) = readAsArrayBuffer b
readUFBAsBase64 :: UploadFileBlob -> Aff String
readUFBAsBase64 (UploadFileBlob b) = do
ab <- readAsArrayBuffer b
pure $ arrayBufferToBase64 ab
--pure $ Base64.runBase64 $ Base64.encodeBase64 ab
--at <- readAsText b
--pure $ SBase64.encode at
readUFBAsText :: UploadFileBlob -> Aff String
readUFBAsText (UploadFileBlob b) = readAsText b
......@@ -10,7 +10,7 @@ import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Effect.Timer (clearInterval, setInterval)
import Gargantext.Config.REST (RESTError)
import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Config.Utils (handleErrorInAsyncProgress, handleRESTError)
import Gargantext.Routes (SessionRoute(..))
import Gargantext.Sessions (Session, get)
import Gargantext.Types (FrontendError)
......@@ -60,6 +60,7 @@ asyncProgressBarCpt = here.component "asyncProgressBar" cpt
_ <- case R.readRef intervalIdRef of
Nothing -> pure unit
Just iid -> clearInterval iid
handleErrorInAsyncProgress errors asyncProgress
onFinish unit
else
pure unit
......
......@@ -363,11 +363,11 @@ forceAtlas2Settings =
, batchEdgesDrawing : true
, edgeWeightInfluence : 1.0
-- fixedY : false
, gravity : 0.01
, gravity : 1.0
, hideEdgesOnMove : true
, includeHiddenEdges : false
, includeHiddenNodes : true
, iterationsPerRender : 50.0 -- 10.0
, iterationsPerRender : 100.0 -- 10.0
, linLogMode : false -- false
, outboundAttractionDistribution : false
, scalingRatio : 1000.0
......
......@@ -17,7 +17,7 @@ import Effect.Now as EN
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryDataURL)
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryData)
import Gargantext.Components.GraphExplorer.API (cloneGraph)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.GraphExplorer.Utils as GEU
......@@ -99,7 +99,7 @@ cameraButton { id
case eClonedGraphId of
Left err -> liftEffect $ log2 "[cameraButton] RESTError" err
Right clonedGraphId -> do
eRet <- uploadArbitraryDataURL session clonedGraphId (Just $ nowStr <> "-" <> "screenshot.png") screen
eRet <- uploadArbitraryData session clonedGraphId (Just $ nowStr <> "-" <> "screenshot.png") screen
case eRet of
Left err -> liftEffect $ log2 "[cameraButton] RESTError" err
Right _ret -> do
......
......@@ -289,8 +289,8 @@ loadedNgramsTableHeaderCpt :: R.Component LoadedNgramsTableHeaderProps
loadedNgramsTableHeaderCpt = here.component "loadedNgramsTableHeader" cpt where
cpt { searchQuery } _ = do
pure $ R.fragment
[ H.h4 {style: {textAlign : "center"}}
[ H.span {className: "fa fa-hand-o-down"} []
[ H.h4 { style: { textAlign : "center" } }
[ H.span { className: "fa fa-hand-o-down" } []
, H.text "Extracted Terms" ]
, NTC.searchInput { key: "search-input"
, searchQuery }
......
......@@ -36,9 +36,9 @@ searchInputCpt :: R.Component SearchInputProps
searchInputCpt = here.component "searchInput" cpt
where
cpt { searchQuery } _ = do
pure $ R2.row [
H.div { className: "col-12" } [
H.div { className: "input-group" }
pure $ R2.row
[ H.div { className: "col-12" }
[ H.div { className: "input-group" }
[ searchButton { searchQuery } []
, searchFieldInput { searchQuery } []
]
......
......@@ -105,7 +105,7 @@ frameLayoutViewCpt = here.component "frameLayoutView" cpt
Just url -> pure $ nodeFrameVisio { frame_id, reload, url }
_ ->
pure $ H.div{} [
FV.backButton
FV.backButton {} []
, H.div { className : "frame"
, rows: "100%,*" }
[ -- H.script { src: "https://visio.gargantext.org/external_api.js"} [],
......
......@@ -64,7 +64,7 @@ tableHeaderLayoutCpt = here.component "tableHeaderLayout" cpt
cacheState' <- T.useLive T.unequal cacheState
pure $ R.fragment
[ R2.row [FV.backButton]
[ R2.row [FV.backButton {} []]
,
R2.row
[ H.div {className: "col-md-3"} [ H.h3 {} [H.text title] ]
......
......@@ -133,7 +133,7 @@ divDropdownLeftCpt = here.component "divDropdownLeft" cpt
, text : "Source Code Documentation"
}
, LiNav { title : "API documentation"
, href : "https://v4.gargantext.org/swagger-ui"
, href : "https://cnrs.gargantext.org/swagger-ui"
, icon : "fa fa-code-fork"
, text : "API documentation"
}
......
......@@ -4,10 +4,12 @@ import Gargantext.Prelude
import Data.Array as A
import Data.Either (Either(..))
import Data.Foldable (foldl)
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Gargantext.Config.REST (RESTError)
import Gargantext.Types (FrontendError(..))
import Gargantext.Types (AsyncEvent(..), AsyncProgress(..), AsyncTaskLog(..), AsyncTaskStatus(..), FrontendError(..))
import Gargantext.Utils.Reactix as R2
import Toestand as T
......@@ -22,3 +24,24 @@ handleRESTError errors (Left error) _ = liftEffect $ do
T.modify_ (A.cons $ FRESTError { error }) errors
here.log2 "[handleTaskError] RESTError" error
handleRESTError _ (Right task) handler = handler task
handleErrorInAsyncProgress :: T.Box (Array FrontendError)
-> AsyncProgress
-> Effect Unit
handleErrorInAsyncProgress errors ap@(AsyncProgress { status: IsFailure }) = do
T.modify_ (A.cons $ FStringError { error: concatErrors ap }) errors
handleErrorInAsyncProgress errors ap@(AsyncProgress { log, status: IsFinished }) = do
if countFailed > 0 then
T.modify_ (A.cons $ FStringError { error: concatErrors ap }) errors
else
pure unit
where
countFailed = foldl (+) 0 $ (\(AsyncTaskLog { failed }) -> failed) <$> log
handleErrorInAsyncProgress _ _ = pure unit
concatErrors :: AsyncProgress -> String
concatErrors (AsyncProgress { log }) = foldl eventsErrorMessage "" log
where
eventsErrorMessage acc (AsyncTaskLog { events }) = (foldl eventErrorMessage "" events) <> "\n" <> acc
eventErrorMessage acc (AsyncEvent { level: "ERROR", message }) = message <> "\n" <> acc
eventErrorMessage acc _ = acc
module Gargantext.Hooks.LinkHandler
( useLinkHandler
( Methods, useLinkHandler
, goToRoute, goToURL, goToPreviousPage
) where
......
......@@ -14,16 +14,15 @@ import Data.Show.Generic (genericShow)
import Data.String as S
import Effect.Aff (Aff)
import Foreign as F
import Gargantext.Components.Lang (class Translate, Lang(..))
import Gargantext.Config.REST (RESTError)
import Gargantext.Utils.Glyphicon (classNamePrefix, glyphiconToCharCode)
import Prim.Row (class Union)
import Reactix as R
import Simple.JSON as JSON
import Simple.JSON.Generics as JSONG
import URI.Query (Query)
import Gargantext.Components.Lang (class Translate, Lang(..))
import Gargantext.Config.REST (RESTError)
import Gargantext.Utils.Glyphicon (classNamePrefix, glyphiconToCharCode)
data Handed = LeftHanded | RightHanded
switchHanded :: forall a. a -> a -> Handed -> a
......@@ -659,7 +658,6 @@ data AsyncTaskType = AddNode
| GraphRecompute
| ListUpload
| ListCSVUpload -- legacy v3 CSV upload for lists
| ListZIPUpload
| Query
| UpdateNgramsCharts
| UpdateNode
......@@ -680,7 +678,6 @@ asyncTaskTypePath CorpusFormUpload = "add/form/async/"
asyncTaskTypePath GraphRecompute = "async/recompute/"
asyncTaskTypePath ListUpload = "add/form/async/"
asyncTaskTypePath ListCSVUpload = "csv/add/form/async/"
asyncTaskTypePath ListZIPUpload = "zip/add/form/async/"
asyncTaskTypePath Query = "query/"
asyncTaskTypePath UpdateNgramsCharts = "ngrams/async/charts/update/"
asyncTaskTypePath UpdateNode = "update/"
......@@ -723,18 +720,17 @@ derive instance Newtype AsyncTask _
derive newtype instance JSON.ReadForeign AsyncTask
instance Eq AsyncTask where eq = genericEq
newtype AsyncTaskWithType = AsyncTaskWithType {
task :: AsyncTask
newtype AsyncTaskWithType = AsyncTaskWithType
{ task :: AsyncTask
, typ :: AsyncTaskType
}
derive instance Generic AsyncTaskWithType _
derive instance Newtype AsyncTaskWithType _
derive newtype instance JSON.ReadForeign AsyncTaskWithType
instance Eq AsyncTaskWithType where
eq = genericEq
instance Eq AsyncTaskWithType where eq = genericEq
newtype AsyncProgress = AsyncProgress {
id :: AsyncTaskID
newtype AsyncProgress = AsyncProgress
{ id :: AsyncTaskID
, log :: Array AsyncTaskLog
, status :: AsyncTaskStatus
}
......@@ -742,8 +738,16 @@ derive instance Generic AsyncProgress _
derive instance Newtype AsyncProgress _
derive newtype instance JSON.ReadForeign AsyncProgress
newtype AsyncTaskLog = AsyncTaskLog {
events :: Array String
newtype AsyncEvent = AsyncEvent
{ level :: String
, message :: String
}
derive instance Generic AsyncEvent _
derive instance Newtype AsyncEvent _
derive newtype instance JSON.ReadForeign AsyncEvent
newtype AsyncTaskLog = AsyncTaskLog
{ events :: Array AsyncEvent
, failed :: Int
, remaining :: Int
, succeeded :: Int
......@@ -753,7 +757,7 @@ derive instance Newtype AsyncTaskLog _
derive newtype instance JSON.ReadForeign AsyncTaskLog
progressPercent :: AsyncProgress -> Number
progressPercent (AsyncProgress {log}) = perc
progressPercent (AsyncProgress { log }) = perc
where
perc = case A.head log of
Nothing -> 0.0
......@@ -783,10 +787,9 @@ toggleSidePanelState Opened = Closed
---------------------------------------------------------------------------
data FrontendError = FStringError
{ error :: String
} | FRESTError
{ error :: RESTError }
data FrontendError =
FStringError { error :: String }
| FRESTError { error :: RESTError }
derive instance Generic FrontendError _
instance Eq FrontendError where eq = genericEq
// shameless copy from
// https://stackoverflow.com/a/9458996
// This is because base64-codec/Data.Base64.encodeBase64 was too slow
exports.arrayBufferToBase64Impl = function(buffer) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
module Gargantext.Utils.ArrayBuffer where
import Data.ArrayBuffer.Types (ArrayBuffer)
import Data.Function.Uncurried (Fn1, runFn1)
foreign import arrayBufferToBase64Impl :: Fn1 ArrayBuffer String
arrayBufferToBase64 :: ArrayBuffer -> String
arrayBufferToBase64 = runFn1 arrayBufferToBase64Impl
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment