Commit 419ef08b authored by Karen Konou's avatar Karen Konou

Merge branch 'dev' into 711-home-when-user-is-logged-display-an-easy-entry-to-create-a-corpus

parents 19692b60 b1406873
Pipeline #7977 passed with stages
in 44 minutes and 59 seconds
## Version 0.0.7.5.3
* [BACK][FIX][Resolve "[Server slowness] With the dev branch on the dev instance, we're experiencing a real slowness" (JobInfo changes)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/429)
* [BACK][OPTIM][Concurrent queries in NLP](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/451)
* [FRONT][FIX][[graph] restore proportional labels and make edges transparent](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/522)
* [BACK][FIX][Resolve "On IMT Instance : Error message or crashes when changing the status of terms in large batch"](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/445)
* [FRONT][FIX][graph: rework edge renderer based on rectangle renderer](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/523)
* [BACK][FIX][Break loops in Ngrams graphs](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/453)
## Version 0.0.7.5.2
* [BACK/FRONT][FIX][[query] a prefixed query like ~prefix should transform to 'prefix', not '"prefix'](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/443)
* [BACK][OPTIM][Separate ngram extraction from document insertion](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/415)
* [BACK][FIX][Keep only the roots in searchTableNgrams](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/439)
* [BACK/FRONT][OPTIM][Dev add option to notify users](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/438) and [[notifications] add possibility to notify user from the backend](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/520)
* [BACK][FIX][Dev worker fixes](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/447)
* [BACK][FIX][Dev allow for api error in flow](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/440)
## Version 0.0.7.5.1
* [BACK/FRONT][FIX][[search] small refactoring of the search API](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/441) and [[query] a prefixed query like ~prefix should transform to 'prefix', not '"prefix'](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/443)
* [BACK][FIX][[CLI] db fix command, to fix hyperdata #630](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/505)
* [BACK][OPTIM][Add the ability to emit logging messages from a `DbTx` transaction](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/436)
* [BACK][OPTIM][Prevent importing ngrams which will lead to loops](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/435)
* [FRONT][FIX][[graph] fix updateGraph function so that labels are rendered properly](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/521)
## Version 0.0.7.5
* [BACK][UPGRADE][Allow ngrams to be searched even if they appear deeply nested](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/433)
* [BACK][FIX][[cli] add support to spawn multiple workers of the same type](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/437)
* [FRONT][FIX][Resolve "Problem with related terms"](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/518)
* [BACK/FRONT][OPTIM][[API] version with git hash](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/422) and [Resolve "Inject commit hash in the version popup"](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/517)
## Version 0.0.7.4.9
* [FRONT][FIX][Dev support emitting warnings](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/519)
* [BACK][FIX][Forest of trees: restore hierarchical grouping of terms](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/424)
* [BACK][UPGRADE][[BREAKING] refactoring of fc_url](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/405)
* [BACK][FIX][Resolve "[Node Terms] On multiple Map terms, if the first term is already a map term, the count of the multiple map term is to zero"](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/414)
* [BACK][UPGRADE][Resolve "Move corenlp to separate repo/flake"](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/434)
## Version 0.0.7.4.8
* [BACK][UPGRADE][Upgrade GHC to 9.6.x (#436)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/436) and [Try to drop dependency on `accelerate-llvm` and the entire `llvm` stack (#291)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/291)
* [BACK][FEAT][Implement temporary file storage (#444)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/444)
* [BACK/FRONT][FEAT][Import/export in SQLite format (#362)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/362)
* [DOC][README][README: add info about system postgresql configuration](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/403)
* [FRONT][FIX][`./install` doesn't work on Darwin (#671)](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/671)
* [BACK][FIX][Fix `start-all` so that it throws exception when a subprocess fails (#463)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/463)
* [FRONT][FIX][[corpus] create list if it doesn't exist](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/506)
* [FRONT][FIX][Upload file sends 'Nothing' as language which results in 400 error from backend (#736)](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/736)
* [BACK][FIX][Convert non-transactional GGTX DB queries into transactions (#411)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/466) and [ACID properties of DB operations](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/411)
* [BACK][FIX][Proper (and thread-safe) implementation for `withSeed`](https://gitlab.iscpif.fr/gargantext/haskell-igraph/merge_requests/6)
* [FRONT][FEAT][Share link copy to clipboard](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/719)
* [BACK][FIX][Relax dependency bounds](https://gitlab.iscpif.fr/gargantext/haskell-igraph/merge_requests/8)
* [BACK][UPGRADE][Upgrade IGraph](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/411)
* [FRONT][FEAT][Feature flags hide](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/511)
* [BACK][FIX][Fix a bug in `buildPatterns` and friends](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/413)
* [BACK/FRONT][OPTIM][Resolve "Display graph parameters in legend"](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/510)
* [BACK][FIX][Separate ngram extraction from document insertion](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/415)
* [BACK][REFACT][API refactorings (#467)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/467)
* [BACK/FRONT][FIX][[graphql] simplify and_logic for get contexts for ngrams](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/515)
* [FRONT][OPTIM][[Node Graph] CSS improvements on the legend tab (#743)](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/743)
* [BACK][FIX][ Have `extractNgramsFromDocument` catch the right exception in case extraction fails](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/419)
* [FRONT][FIX][[Node terms] Show related docs for children terms as well](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/merge_requests/516)
* [BACK][FIX][Fix bug in DB transaction rollbacks in the presence of domain-specific errors](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/420)
* [BACK][FIX][Resolve "Error uploading zip file on dev"](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/417)
* [BACK][OPTIM][Port all the tasty specs to hspec](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/423)
* [BACK][UPGRADE][[openalex] make search act similar to the one on openalex web](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/425)
* [BACK][FIX][Proper incremental TSV parser](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/merge_requests/402)
* [BACK][OPTIM][Remove useless ghc dependency from tree](https://gitlab.iscpif.fr/gargantext/crawlers/pubmed/merge_requests/14)
## Version 0.0.7.4.7
* [BACK][FIX][Adjust the output of the UpdateList tests (#460)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/460)
......
......@@ -110,3 +110,16 @@ nix-shell --run "npm run css"
```
#### Feature flags
Some functionality is hidden behind feature flags (mostly it's because
the work is in progress). To enable "expert" mode, issue this in your
JS console:
```javascript
document.cookie = 'expert=true'
```
To unset expert mode:
```javascript
document.cookie = 'expert=false'
```
......@@ -36,16 +36,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1731603435,
"narHash": "sha256-CqCX4JG7UiHvkrBTpYC3wcEurvbtTADLbo3Ns2CEoL8=",
"lastModified": 1748026580,
"narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8b27c1239e5c421a2bbc2c65d52e4a6fbf2ff296",
"rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "24.11",
"ref": "25.05",
"repo": "nixpkgs",
"type": "github"
}
......@@ -59,11 +59,11 @@
"slimlock": "slimlock"
},
"locked": {
"lastModified": 1739795788,
"narHash": "sha256-PG4lO49fvWHpQoCN3mIGflisXF+5vEf91ceV9hsWTLU=",
"lastModified": 1749721661,
"narHash": "sha256-z0Wfm2ThrUilogAeewLf/0Oc2a/4ODrxrA+4NWAdoAQ=",
"owner": "thomashoneyman",
"repo": "purescript-overlay",
"rev": "f754c2ef94cef46e5d5223c25d3f361e1f6cb509",
"rev": "c73f85b379dcb30f4f3f1bca152b19c880309d01",
"type": "github"
},
"original": {
......
......@@ -3,7 +3,7 @@
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.nixpkgs.url = "github:NixOS/nixpkgs?ref=24.11";
inputs.nixpkgs.url = "github:NixOS/nixpkgs?ref=25.05";
inputs.purescript-overlay = {
url = "github:thomashoneyman/purescript-overlay";
......@@ -20,8 +20,8 @@
};
dependencies = with pkgs; [
purs-bin.purs-0_15_16-4 # from the purescript-overlay
spago-bin.spago-0_93_43
purs-bin.purs-0_15_16-5 # from the purescript-overlay
spago-bin.spago-0_93_44
nodePackages.purs-tidy
watchexec
esbuild
......@@ -46,14 +46,6 @@
fi
'';
build-zephyr = pkgs.writeShellScriptBin "build-zephyr" ''
set -e
npm spago build --purs-args '--codegen corefn,js'
zephyr -f Main.main
browserify-zephyr
'';
minify-bundle = pkgs.writeShellScriptBin "minify-bundle" ''
set -e
......@@ -124,7 +116,9 @@
# compile
echo "Bundling"
echo "{\"commit_hash\": \"$(git rev-parse HEAD)\"}" > .commit-hash.json
npm run bundle
rm .commit-hash.json
'';
};
test-ps = pkgs.writeShellApplication {
......@@ -171,7 +165,6 @@
self.packages.${system}.test-ps
self.packages.${system}.repl
setup-gitblame
build-zephyr
minify-bundle
serve
]);
......
{
"name": "GarganText",
"version": "0.0.7.4.7",
"version": "0.0.7.5.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "GarganText",
"version": "0.0.7.4.7",
"version": "0.0.7.5.3",
"dependencies": {
"@fontsource/crete-round": "~5.0.12",
"@fontsource/montserrat": "~5.0.17",
......@@ -21,6 +21,7 @@
"bootstrap": "~4.6.0",
"bootstrap-dark": "~1.0.3",
"buffer": "~6.0.3",
"chroma-js": "^3.1.2",
"clipboard": "^2.0.11",
"concurrently": "^9.1.0",
"create-react-class": "~15.6.3",
......@@ -5245,6 +5246,12 @@
"fsevents": "~2.3.2"
}
},
"node_modules/chroma-js": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-3.1.2.tgz",
"integrity": "sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg==",
"license": "(BSD-3-Clause AND Apache-2.0)"
},
"node_modules/chrome-remote-interface": {
"version": "0.31.3",
"dev": true,
......
{
"name": "GarganText",
"version": "0.0.7.4.7",
"version": "0.0.7.5.3",
"scripts": {
"build": "spago build",
"bundle": "spago bundle --module Main --outfile dist/bundle.min.js --minify --source-maps",
......@@ -38,6 +38,7 @@
"bootstrap": "~4.6.0",
"bootstrap-dark": "~1.0.3",
"buffer": "~6.0.3",
"chroma-js": "^3.1.2",
"clipboard": "^2.0.11",
"concurrently": "^9.1.0",
"create-react-class": "~15.6.3",
......@@ -58,8 +59,8 @@
"http-proxy-middleware": "^3.0.3",
"immer": "~9.0.5",
"isomorphic-unfetch": "~3.1.0",
"jszip": "^3.10.1",
"js-cookie": "^3.0.5",
"jszip": "^3.10.1",
"markdown-it": "~13.0.1",
"minify": "^11.3.0",
"prop-types": "~15.6.2",
......
......@@ -2,6 +2,7 @@ module Gargantext.Components.App (app) where
import Gargantext.Prelude
import Data.Array as A
import Data.Sequence as Seq
import Data.Tuple.Nested ((/\))
import Gargantext.AsyncTasks as GAT
......@@ -14,7 +15,7 @@ import Gargantext.Hooks (useHashRouter)
import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Router as Router
import Gargantext.Sessions as Sessions
import Gargantext.Types (CacheParams, defaultCacheParams)
import Gargantext.Types (CacheParams, defaultCacheParams, FrontendError(FStringNotification))
import Gargantext.Utils (getter, host)
import Gargantext.Utils.Reactix as R2
import Reactix as R
......@@ -122,6 +123,15 @@ mainAppCpt = here.component "main" cpt
wsProto <- Notifications.wsProtocol
h <- host
Notifications.connect ws (wsProto <> "://" <> h <> "/ws") session
-- Subscribe to ping requests (e.g. NotifyUser)
let
callback n = case n of
NotificationsT.NNotifyUser _userId notification -> T.modify_ (A.cons $ FStringNotification { notification }) boxes.errors
_ -> here.log2 "ping received but not handled" n
let action = NotificationsT.InsertCallback NotificationsT.Ping "ping!" callback
Notifications.performAction ws action
-- T.write_ ws boxes.wsNotification
-- NOTE: Dummy subscription
-- let action = NotificationsT.InsertCallback (NotificationsT.UpdateTree (-1)) "some-uuid" (\_ -> here.log "callback!")
......
......@@ -36,53 +36,21 @@ componentCpt = here.component "main" cpt
{}
(mapWithIndex (showError errors) errors')
showError errors i (FStringError { error }) =
RB.alert
{ dismissible: true
, onClose
, variant: "danger"
}
[ H.text error ]
where
onClose = do
here.error2 "click!" error
T.modify_
( \es -> case deleteAt i es of
Nothing -> es
Just es' -> es'
)
errors
showError errors i (FRESTError { error }) =
RB.alert
{ dismissible: true
, onClose
, variant: "danger"
}
[ H.text $ show error ]
where
onClose = do
here.error2 "click!" error
T.modify_
( \es -> case deleteAt i es of
Nothing -> es
Just es' -> es'
)
errors
showError errors i (FStringError { error }) = errorAlert errors i "danger" error
showError errors i (FStringWarning { warning }) = errorAlert errors i "warning" warning
showError errors i (FStringNotification { notification }) = errorAlert errors i "info" notification
showError errors i (FRESTError { error }) = errorAlert errors i "danger" (show error)
showError errors i (FOtherError { error }) = errorAlert errors i "danger" (show error)
showError errors i (FOtherError { error }) =
errorAlert errors i variant txt =
RB.alert
{ dismissible: true
, onClose
, variant: "danger"
, variant
}
[ H.text $ show error ]
[ H.text txt ]
where
onClose = do
here.error2 "click!" error
T.modify_
( \es -> case deleteAt i es of
Nothing -> es
......
......@@ -11,6 +11,7 @@ import Data.Foldable (intercalate)
import Data.Map as Map
import Data.Maybe (Maybe(..), maybe)
import Data.String.Regex as Regex
import Data.Tuple (Tuple(..))
import Effect (Effect)
import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect)
......@@ -36,6 +37,7 @@ import Gargantext.Context.Progress (asyncContext, asyncProgress)
import Gargantext.Ends (Frontends, url)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Hooks.UseFeatureFlag as Feature
import Gargantext.Hooks.Version (Version, useVersion)
import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, sessionId)
......@@ -738,7 +740,9 @@ listNodeActionsLoadedCpt = here.component "listNodeActionsLoaded" cpt
type VersionComparatorProps =
( clientVersion :: Version
, clientCommit :: String
, remoteVersion :: Version
, remoteCommit :: String
)
versionComparator :: R2.Leaf VersionComparatorProps
......@@ -747,41 +751,46 @@ versionComparator = R2.leaf versionComparatorCpt
versionComparatorCpt :: R.Component VersionComparatorProps
versionComparatorCpt = here.component "versionComparator" cpt
where
cpt { clientVersion, remoteVersion } _
| clientVersion == remoteVersion = pure $
B.caveat
{ variant: Success
, className: "mainleaf__version-comparator"
}
[ B.b_ "Versions match"
, content clientVersion remoteVersion
]
| otherwise = pure $
B.caveat
{ variant: Warning
, className: "mainleaf__version-comparator"
}
[ B.b_ "Versions mismatch"
, content clientVersion remoteVersion
cpt { clientCommit, clientVersion, remoteVersion, remoteCommit } _ = do
let
Tuple variant msg =
if clientVersion == remoteVersion then Tuple Success "Versions match"
else Tuple Warning "Versions mismatch"
commitEl hash =
H.span {}
[ H.text " ("
, B.code_ hash
, H.text ")"
]
content :: Version -> Version -> R.Element
content clientVersion remoteVersion =
H.ul
{}
[ H.li
{}
[ B.span_ "frontend: "
, H.text $ nbsp 1
, B.code_ clientVersion
]
, H.li
{}
[ B.span_ "backend: "
, H.text $ nbsp 1
, B.code_ remoteVersion
]
]
pure $
B.caveat
{ variant
, className: "mainleaf__version-comparator"
}
[ B.b_ msg
, H.ul {}
[ H.li {}
[ B.span_ "frontend: "
, H.text $ nbsp 1
, B.code_ clientVersion
, Feature.hide
{ keys: [ "expert" ]
, render: commitEl clientCommit
}
]
, H.li {}
[ B.span_ "backend: "
, H.text $ nbsp 1
, B.code_ remoteVersion
, Feature.hide
{ keys: [ "expert" ]
, render: commitEl remoteCommit
}
]
]
]
-------------------------------------------------------
......
......@@ -216,6 +216,9 @@ drawGraphCpt = here.component "drawGraph" cpt
, edgeWeight: edgeWeight'
, showEdges: showEdges'
}
-- #512 make sure labels are rendered according to forceAtlasState
let renderLabels = SigmaxTypes.forceAtlasLabelState forceAtlasState'
Sigma.setSettings sigma { renderLabels }
-- TODO This is a temporary solution that seems to fix
-- blank page of graph when there are too many edges. It
......
......@@ -21,6 +21,7 @@ import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Effect.Timer (setTimeout)
import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Elevation(..), Variant(..))
......@@ -41,6 +42,7 @@ import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Sessions (Session)
import Gargantext.Types (CTabNgramType, FrontendError(..), NodeID, SidePanelState(..), TabSubType(..), TabType(..), TermList(..), modeTabType)
import Gargantext.Utils (getter, nbsp, setter, (?))
import Gargantext.Utils.Clipboard (modalClipboard)
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Partial.Unsafe (unsafePartial)
......@@ -487,6 +489,9 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt
termCount /\ termCountBox <-
R2.useBox' 0
copied <- T.useBox false
copied' <- T.useLive T.unequal copied
-- | Computed
-- |
let
......@@ -513,13 +518,18 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt
flip T.listen expandNeighborhood onExpandNeighborhoodChange
R.useEffect1' selectedNodeIds' do
let refreshed = neighbourBadges graph' selectedNodeIds'
let count = Seq.length refreshed
let ordered = A.sortWith (\n -> -n.size) $ Seq.toUnfoldable refreshed
T.write_ (count - 1) termCountBox
T.write_ ordered termListBox
let neighbours' = SigmaxT.neighborsSortedByEdgeWeight graph' selectedNodeIds'
let count = A.length neighbours'
T.write_ count termCountBox
T.write_ neighbours' termListBox
T.write_ false showMoreBox
R.useEffect' $ do
let
labels = (_.label) <$> termList
modalClipboard ".copy" (intercalate "\n" labels)
-- | Render
-- |
pure $
......@@ -541,6 +551,15 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt
[ "text-info", "d-inline" ] $
show termCount
, H.text $ nbsp 1 <> "related terms"
, B.iconButton
{ name: if copied' then "check" else "copy"
, title: if copied' then "Copied" else "Copy terms to clipboard"
, className: "copy copy-btn"
, callback: \_ -> do
T.write_ true copied
_ <- setTimeout 2000 (T.write_ false copied)
pure unit
}
,
-- Expand word cloud
B.iconButton
......@@ -684,11 +703,6 @@ badgeSize minSize maxSize size =
badges :: SigmaxT.SGraph -> SigmaxT.NodeIds -> Seq.Seq (Record SigmaxT.Node)
badges graph selectedNodeIds = SigmaxT.graphNodes $ SigmaxT.nodesById graph selectedNodeIds
neighbourBadges :: SigmaxT.SGraph -> SigmaxT.NodeIds -> Seq.Seq (Record SigmaxT.Node)
neighbourBadges graph selectedNodeIds = SigmaxT.neighbors graph selectedNodes'
where
selectedNodes' = SigmaxT.graphNodes $ SigmaxT.nodesById graph selectedNodeIds
---------------------------------------------------------
type SendPatches =
......
......@@ -142,7 +142,7 @@ performAction ws (RemoveCallback topic uuid) = do
-- WSNotification $ ws' { state = removeCallback ws'.state topic uuid }
performAction (WSNotification ws') (Call notification) = do
state <- Ref.read ws'.state
-- here.log2 "[performAction Call] state" state
-- here.log2 "[performAction Call] notification" notification
callNotification state notification
-- | Correctly choose between "ws" and "wss" protocols based on what
......
......@@ -18,6 +18,7 @@ import Effect.Timer (setTimeout)
import Effect.Var (($=))
import Effect.Var as Var
import Foreign as F
import Gargantext.Components.Login.Types (UserId)
import Gargantext.Sessions.Types (Session(..))
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
......@@ -37,6 +38,7 @@ type UUID = String
data Topic
= UpdateWorkerProgress GT.WorkerTask
| UpdateTree NodeId
| Ping
derive instance Generic Topic _
instance Eq Topic where
......@@ -58,6 +60,8 @@ instance JSON.ReadForeign Topic where
"update_tree" -> do
{ node_id } <- JSON.readImpl f :: F.F { node_id :: NodeId }
pure $ UpdateTree node_id
"ping" -> do
pure Ping
s -> F.fail $ F.ErrorAtProperty "type" $ F.ForeignError $ "unknown Topic type: " <> s
instance JSON.WriteForeign Topic where
......@@ -69,6 +73,9 @@ instance JSON.WriteForeign Topic where
{ "type": "update_tree"
, node_id
}
writeImpl Ping = JSON.writeImpl
{ "type": "ping"
}
data WSRequest
= WSSubscribe Topic
......@@ -98,6 +105,7 @@ instance JSON.WriteForeign WSRequest where
data Notification
= NUpdateWorkerProgress GT.WorkerTask GT.AsyncTaskLog
| NUpdateTree NodeId
| NNotifyUser UserId String
derive instance Generic Notification _
instance JSON.ReadForeign Notification where
......@@ -110,6 +118,9 @@ instance JSON.ReadForeign Notification where
"update_tree" -> do
{ node_id } <- JSON.readImpl f :: F.F { node_id :: NodeId }
pure $ NUpdateTree node_id
"notify_user" -> do
{ user_id, message } <- JSON.readImpl f :: F.F { user_id :: UserId, message :: String }
pure $ NNotifyUser user_id message
s -> F.fail $ F.ErrorAtProperty "type" $ F.ForeignError $ "unkown type: " <> s
notificationTopics :: Notification -> Array Topic
......@@ -123,6 +134,7 @@ notificationTopics (NUpdateWorkerProgress workerTask@(GT.WorkerTask { node_id })
Nothing -> []
Just nId -> [ UpdateTree nId ]
notificationTopics (NUpdateTree nodeId) = [ UpdateTree nodeId ]
notificationTopics (NNotifyUser _ _) = [ Ping ]
type Callback = Notification -> Effect Unit
......
......@@ -6,11 +6,12 @@ import Data.Array as A
import Data.Either (Either(..))
import Data.Foldable (foldl)
import Data.Maybe (fromMaybe, Maybe(..))
import Data.Traversable (traverse_)
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Gargantext.Config.REST (RESTError, logRESTError)
import Gargantext.Types (AsyncEvent(..), AsyncTaskLog(..), FrontendError(..), asyncTaskLogEventsErrorMessage)
import Gargantext.Types (AsyncEvent(..), AsyncTaskLog(..), FrontendError(..), asyncErrorToFrontendError, asyncTaskLogEventsErrors)
import Gargantext.Utils.Reactix as R2
import Toestand as T
......@@ -54,7 +55,4 @@ handleErrorInAsyncTaskLog
-> AsyncTaskLog
-> Effect Unit
handleErrorInAsyncTaskLog errors atl =
case asyncTaskLogEventsErrorMessage atl of
Nothing -> pure unit
Just error ->
T.modify_ (A.cons $ FStringError { error }) errors
traverse_ (\e -> T.modify_ (A.cons $ asyncErrorToFrontendError e) errors) (asyncTaskLogEventsErrors atl)
......@@ -4,7 +4,7 @@ import Graph from 'graphology';
import Sigma from 'sigma';
//import { takeScreenshot } from '../../src/external-deps/sigmajs-screenshot.js';
import takeScreenshot from '../../src/external-deps/sigmajs-screenshot-with-canvas.js';
import { NodeCircleProgram } from 'sigma/rendering';
import { NodeCircleProgram, EdgeRectangleProgram } from 'sigma/rendering';
import ContourCircleNodeProgram from '../../src/external-deps/sigmajs-circle-with-contour.js';
import TriangleNodeProgram from '../../src/external-deps/sigmajs-triangle.js';
import ContourTriangleNodeProgram from '../../src/external-deps/sigmajs-triangle-with-contour.js';
......@@ -12,6 +12,7 @@ import SquareNodeProgram from '../../src/external-deps/sigmajs-square.js';
import ContourSquareNodeProgram from '../../src/external-deps/sigmajs-square-with-contour.js';
import DiamondNodeProgram from '../../src/external-deps/sigmajs-diamond.js';
import ContourDiamondNodeProgram from '../../src/external-deps/sigmajs-diamond-with-contour.js';
import EdgeRectangleTransparentProgram from '../../src/external-deps/sigmajs-rectangle-transparent.js';
let sigma = Sigma.Sigma;
console.log('imported sigma', Sigma);
......@@ -196,7 +197,7 @@ function _sigma(left, right, el, opts) {
try {
let graph = new Graph();
const settings = {
labelRenderer: drawLabel,
defaultDrawNodeLabel: drawLabel,
nodeProgramClasses: {
circle: NodeCircleProgram,
ccircle: ContourCircleNodeProgram,
......@@ -207,6 +208,9 @@ function _sigma(left, right, el, opts) {
diamond: DiamondNodeProgram,
cdiamond: ContourDiamondNodeProgram
},
edgeProgramClasses: {
line: EdgeRectangleTransparentProgram
},
...opts.settings
};
let s = new Sigma(graph, el, settings);
......
......@@ -18,7 +18,7 @@ import Gargantext.Data.Louvain as Louvain
import Gargantext.Types as GT
import Gargantext.Utils.Range as Range
import Partial.Unsafe (unsafePartial)
import Prelude (class Eq, class Show, ($), (&&), (==), (||), (<$>), mod, not, (<=))
import Prelude (class Eq, class Show, ($), (&&), (==), (||), (<$>), mod, not, (<=), negate)
import Record.Unsafe (unsafeGet, unsafeSet)
newtype Graph n e = Graph { edges :: Seq.Seq { | e }, nodes :: Seq.Seq { | n } }
......@@ -214,11 +214,13 @@ sub graph (Graph { nodes, edges }) = newGraph
filteredEdges = edgesFilter edgeFilterFunc graph
newGraph = nodesFilter (\n -> not (Set.member n.id nodeIds)) filteredEdges
neighbors :: SGraph -> Seq.Seq (Record Node) -> Seq.Seq (Record Node)
neighbors g nodes = Seq.fromFoldable $ Set.unions [ if Set.size sources <= 1 then targets else sources ]
-- | NOTE: The logic of this function is a bit iffy. See
-- https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/749
neighbors :: SGraph -> NodeIds -> Set.Set (Record Node)
neighbors g nodeIds = Set.unions [ if Set.size sources <= 1 then targets else sources ]
where
nodeIds = Set.fromFoldable $ Seq.map _.id nodes
selectedEdges = neighboringEdges g nodeIds
nodeIds' = Set.fromFoldable nodeIds
selectedEdges = neighboringEdges g nodeIds'
sources = Set.fromFoldable $ graphNodes $ nodesById g $ Set.fromFoldable $ Seq.map _.source selectedEdges
targets = Set.fromFoldable $ graphNodes $ nodesById g $ Set.fromFoldable $ Seq.map _.target selectedEdges
......@@ -227,6 +229,12 @@ neighboringEdges g nodeIds = Seq.filter condition $ graphEdges g
where
condition { source, target } = (Set.member source nodeIds) || (Set.member target nodeIds)
neighborsSortedByEdgeWeight :: SGraph -> NodeIds -> Array (Record Node)
neighborsSortedByEdgeWeight g nodeIds = A.nubByEq (\n1 n2 -> n1.id == n2.id) neighbourNodes
where
neighbourNodes = (\e -> if Set.member e.source nodeIds then e.targetNode else e.sourceNode) <$> edgesSorted
edgesSorted = A.sortWith (\e -> -e.weight) $ A.fromFoldable (neighboringEdges g nodeIds)
eqGraph :: SGraph -> SGraph -> Boolean
eqGraph (Graph { nodes: n1, edges: e1 }) (Graph { nodes: n2, edges: e2 }) = (n1 == n2) && (e1 == e2)
......
'use strict';
import pkg from '../../package.json';
import ch from '../../.commit-hash.json';
let version = pkg.version
let version = pkg.version;
let commitHash = ch.commit_hash;
export { version };
export { version, commitHash };
......@@ -22,6 +22,7 @@ import Toestand as T
-- | (ie. Frontend Version)
foreign import version :: Version
foreign import commitHash :: String
type Version = String
......@@ -33,7 +34,9 @@ type R_Input =
type Output = Maybe R_Output
type R_Output =
{ clientVersion :: String
, clientCommit :: String
, remoteVersion :: String
, remoteCommit :: String
}
-- | Conditional Hooks checking release version match between client and remove
......@@ -49,7 +52,9 @@ useVersion mInput = do
Left err -> liftEffect $ log2 "[version] error" err
Right v -> liftEffect $ flip T.write_ mOutputBox $ Just
{ clientVersion: version
, remoteVersion: v
, clientCommit: commitHash
, remoteVersion: v.version
, remoteCommit: v.commitHash
}
-- Hooks
useFirstEffect' $ case mInput of
......@@ -58,5 +63,5 @@ useVersion mInput = do
-- Output
pure mOutput
getBackendVersion :: Session -> REST.AffRESTError Version
getBackendVersion :: Session -> REST.AffRESTError { version :: Version, commitHash :: String }
getBackendVersion (Session { backend }) = REST.get Nothing (toUrl backend "version")
......@@ -838,8 +838,17 @@ derive instance Generic AsyncEvent _
derive instance Newtype AsyncEvent _
derive newtype instance JSON.ReadForeign AsyncEvent
asyncEventErrorMessage :: AsyncEvent -> Maybe String
asyncEventErrorMessage (AsyncEvent { level: "ERROR", message }) = Just message
data AsyncError
= AsyncErrorMessage String
| AsyncWarningMessage String
asyncErrorToFrontendError :: AsyncError -> FrontendError
asyncErrorToFrontendError (AsyncErrorMessage error) = FStringError { error }
asyncErrorToFrontendError (AsyncWarningMessage warning) = FStringWarning { warning }
asyncEventErrorMessage :: AsyncEvent -> Maybe AsyncError
asyncEventErrorMessage (AsyncEvent { level: "ERROR", message }) = Just $ AsyncErrorMessage message
asyncEventErrorMessage (AsyncEvent { level: "WARNING", message }) = Just $ AsyncWarningMessage message
asyncEventErrorMessage _ = Nothing
newtype AsyncTaskLog = AsyncTaskLog
......@@ -853,17 +862,9 @@ derive instance Generic AsyncTaskLog _
derive instance Newtype AsyncTaskLog _
derive newtype instance JSON.ReadForeign AsyncTaskLog
asyncTaskLogEventsErrorMessage :: AsyncTaskLog -> Maybe String
asyncTaskLogEventsErrorMessage (AsyncTaskLog { events }) =
foldl eventErrorMessage' Nothing events
where
eventErrorMessage' acc ae =
case asyncEventErrorMessage ae of
Nothing -> acc
Just e' ->
case acc of
Nothing -> Just e'
Just acc' -> Just $ e' <> "\n" <> acc'
asyncTaskLogEventsErrors :: AsyncTaskLog -> Array AsyncError
asyncTaskLogEventsErrors (AsyncTaskLog { events }) =
A.mapMaybe asyncEventErrorMessage events
asyncTaskLogPercent :: AsyncTaskLog -> Number
asyncTaskLogPercent (AsyncTaskLog { failed, remaining, succeeded }) = 100.0 * nom / denom
......@@ -877,7 +878,7 @@ asyncTaskLogIsFinished (AsyncTaskLog { remaining }) = remaining == 0
asyncTaskLogIsError :: AsyncTaskLog -> Boolean
asyncTaskLogIsError atl@(AsyncTaskLog { events }) =
asyncTaskLogIsFinished atl
&& (A.length $ A.filter (\(AsyncEvent { level }) -> level == "ERROR") events)
&& (A.length $ A.filter (\(AsyncEvent { level }) -> level == "ERROR" || level == "WARNING") events)
> 0
-- New type tasks (async workers)
......@@ -920,6 +921,8 @@ toggleSidePanelState Opened = Closed
data FrontendError
= FStringError { error :: String }
| FStringWarning { warning :: String }
| FStringNotification { notification :: String }
| FRESTError { error :: RESTError }
| FOtherError { error :: String }
......
import ClipboardJS from "clipboard";
export function _modalClipboard(el, url) {
modal_id = document.getElementsByClassName("b-modal modal show")[0].id
modalEl = document.getElementsByClassName("b-modal modal show")[0]
return new ClipboardJS(el, {
container: document.getElementById(modal_id),
container: modalEl,
text: function(trigger) {
return url
}
......
// language=GLSL
const SHADER_SOURCE = /*glsl*/ `
precision mediump float;
varying vec4 v_color;
varying vec2 v_normal;
varying float v_thickness;
varying float v_feather;
const vec4 transparent = vec4(0.0, 0.0, 0.0, 0.0);
void main(void) {
// We only handle antialiasing for normal mode:
#ifdef PICKING_MODE
gl_FragColor = v_color;
#else
float dist = length(v_normal) * v_thickness;
float t = smoothstep(
v_thickness - v_feather,
v_thickness,
dist
);
gl_FragColor = mix(vec4(v_color.rgb * v_color.a, v_color.a), transparent, t);
#endif
}
`;
export default SHADER_SOURCE;
\ No newline at end of file
// language=GLSL
const SHADER_SOURCE = /*glsl*/ `
attribute vec4 a_id;
attribute vec4 a_color;
attribute vec2 a_normal;
attribute float a_normalCoef;
attribute vec2 a_positionStart;
attribute vec2 a_positionEnd;
attribute float a_positionCoef;
uniform mat3 u_matrix;
uniform float u_sizeRatio;
uniform float u_zoomRatio;
uniform float u_pixelRatio;
uniform float u_correctionRatio;
uniform float u_minEdgeThickness;
uniform float u_feather;
varying vec4 v_color;
varying vec2 v_normal;
varying float v_thickness;
varying float v_feather;
const float bias = 255.0 / 254.0;
void main() {
float minThickness = u_minEdgeThickness;
vec2 normal = a_normal * a_normalCoef;
vec2 position = a_positionStart * (1.0 - a_positionCoef) + a_positionEnd * a_positionCoef;
float normalLength = length(normal);
vec2 unitNormal = normal / normalLength;
// We require edges to be at least "minThickness" pixels thick *on screen*
// (so we need to compensate the size ratio):
float pixelsThickness = max(normalLength, minThickness * u_sizeRatio);
// Then, we need to retrieve the normalized thickness of the edge in the WebGL
// referential (in a ([0, 1], [0, 1]) space), using our "magic" correction
// ratio:
float webGLThickness = pixelsThickness * u_correctionRatio / u_sizeRatio;
// Here is the proper position of the vertex
gl_Position = vec4((u_matrix * vec3(position + unitNormal * webGLThickness, 1)).xy, 0, 1);
// For the fragment shader though, we need a thickness that takes the "magic"
// correction ratio into account (as in webGLThickness), but so that the
// antialiasing effect does not depend on the zoom level. So here's yet
// another thickness version:
v_thickness = webGLThickness / u_zoomRatio;
v_normal = unitNormal;
v_feather = u_feather * u_correctionRatio / u_zoomRatio / u_pixelRatio * 2.0;
#ifdef PICKING_MODE
// For picking mode, we use the ID as the color:
v_color = a_id;
#else
// For normal mode, we use the color:
v_color = a_color;
#endif
v_color.a *= bias;
}
`;
export default SHADER_SOURCE;
\ No newline at end of file
/**
* Sigma.js WebGL Renderer Fast Edge Program
* ==========================================
*
* Program rendering edges using GL_LINES which is presumably very fast but
* won't render thickness correctly on some GPUs and has some quirks.
* @module
*/
import { floatColor, rgbaToFloat } from "sigma/utils";
import { EdgeProgram } from "sigma/rendering";
import FRAGMENT_SHADER_SOURCE from "./rect-frag.glsl";
import VERTEX_SHADER_SOURCE from "./rect-vert.glsl";
import chroma from "chroma-js";
const { UNSIGNED_BYTE, FLOAT } = WebGLRenderingContext;
const UNIFORMS = [
"u_matrix",
"u_zoomRatio",
"u_sizeRatio",
"u_correctionRatio",
"u_pixelRatio",
"u_feather",
"u_minEdgeThickness",
]
export default class RectangleLineTransparentProgram extends EdgeProgram {
getDefinition() {
return {
VERTICES: 6,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE,
METHOD: WebGLRenderingContext.TRIANGLES,
UNIFORMS,
ATTRIBUTES: [
{ name: "a_positionStart", size: 2, type: FLOAT },
{ name: "a_positionEnd", size: 2, type: FLOAT },
{ name: "a_normal", size: 2, type: FLOAT },
{ name: "a_color", size: 4, type: UNSIGNED_BYTE, normalized: true },
{ name: "a_id", size: 4, type: UNSIGNED_BYTE, normalized: true },
],
CONSTANT_ATTRIBUTES: [
// If 0, then position will be a_positionStart
// If 2, then position will be a_positionEnd
{ name: "a_positionCoef", size: 1, type: FLOAT },
{ name: "a_normalCoef", size: 1, type: FLOAT },
],
CONSTANT_DATA: [
[0, 1],
[0, -1],
[1, 1],
[1, 1],
[0, -1],
[1, -1],
],
};
}
processVisibleItem(
edgeIndex,
startIndex,
sourceData,
targetData,
data
) {
const array = this.array;
// Parameters for calculating edge thickness
const a = 0.5;
const b = 1;
const thickness = a + b * data.size;
const x1 = sourceData.x;
const y1 = sourceData.y;
const x2 = targetData.x;
const y2 = targetData.y;
// Alpha parameter
const alpha = 0.45;
const colorTransparent = chroma(data.color).alpha(alpha).hex();
const color = floatColor(colorTransparent);
// Computing normals
const dx = x2 - x1;
const dy = y2 - y1;
let len = dx * dx + dy * dy;
let n1 = 0;
let n2 = 0;
if (len) {
len = 1 / Math.sqrt(len);
n1 = -dy * len * thickness;
n2 = dx * len * thickness;
}
array[startIndex++] = x1;
array[startIndex++] = y1;
array[startIndex++] = x2;
array[startIndex++] = y2;
array[startIndex++] = n1;
array[startIndex++] = n2;
array[startIndex++] = color;
array[startIndex++] = edgeIndex;
}
setUniforms(params, { gl, uniformLocations }) {
const { u_matrix, u_zoomRatio, u_feather, u_pixelRatio, u_correctionRatio, u_sizeRatio, u_minEdgeThickness } =
uniformLocations;
gl.uniformMatrix3fv(u_matrix, false, params.matrix);
gl.uniform1f(u_zoomRatio, params.zoomRatio);
gl.uniform1f(u_sizeRatio, params.sizeRatio);
gl.uniform1f(u_correctionRatio, params.correctionRatio);
gl.uniform1f(u_pixelRatio, params.pixelRatio);
gl.uniform1f(u_feather, params.antiAliasingFeather);
gl.uniform1f(u_minEdgeThickness, params.minEdgeThickness);
}
}
\ No newline at end of file
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