@openneuro/app 4.24.2 → 4.25.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/assets/external/brainlife.png +0 -0
- package/src/assets/external/cbrain.png +0 -0
- package/src/assets/external/nemar.png +0 -0
- package/src/scripts/datalad/dataset/dataset-query-fragments.js +0 -1
- package/src/scripts/dataset/__tests__/__snapshots__/snapshot-container.spec.tsx.snap +95 -0
- package/src/scripts/dataset/components/AnalyzeDropdown.tsx +98 -0
- package/src/scripts/dataset/components/__tests__/AnalyzeDropdown.spec.tsx +19 -0
- package/src/scripts/dataset/dataset-query.jsx +0 -18
- package/src/scripts/dataset/draft-container.tsx +0 -5
- package/src/scripts/dataset/snapshot-container.tsx +2 -11
- package/src/scripts/fixtures/dataset-query.ts +0 -2
- package/src/scripts/queries/dataset.ts +0 -1
- package/src/scripts/datalad/subscriptions/__tests__/__snapshots__/files-subscription.spec.jsx.snap +0 -3
- package/src/scripts/datalad/subscriptions/__tests__/files-subscription.spec.jsx +0 -78
- package/src/scripts/datalad/subscriptions/files-subscription.jsx +0 -105
- package/src/scripts/datalad/subscriptions/useDatasetDeletedSubscription.jsx +0 -30
- package/src/scripts/datalad/subscriptions/useDraftSubscription.js +0 -26
- package/src/scripts/datalad/subscriptions/usePermissionsSubscription.js +0 -21
- package/src/scripts/datalad/subscriptions/useSnapshotsUpdatedSubscriptions.js +0 -21
- package/src/scripts/dataset/components/BrainLifeButton.tsx +0 -38
- package/src/scripts/dataset/components/NemarButton.tsx +0 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openneuro/app",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.25.0-alpha.0",
|
|
4
4
|
"description": "React JS web frontend for the OpenNeuro platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "public/client.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"@emotion/react": "11.11.1",
|
|
21
21
|
"@emotion/styled": "11.11.0",
|
|
22
22
|
"@niivue/niivue": "0.34.0",
|
|
23
|
-
"@openneuro/client": "^4.
|
|
24
|
-
"@openneuro/components": "^4.
|
|
23
|
+
"@openneuro/client": "^4.25.0-alpha.0",
|
|
24
|
+
"@openneuro/components": "^4.25.0-alpha.0",
|
|
25
25
|
"@tanstack/react-table": "^8.9.3",
|
|
26
26
|
"bids-validator": "1.14.6",
|
|
27
27
|
"bytes": "^3.0.0",
|
|
@@ -74,5 +74,5 @@
|
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"access": "public"
|
|
76
76
|
},
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "cb86a035f14433d4783991b586c0ab22f58ec7d8"
|
|
78
78
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -302,6 +302,99 @@ exports[`SnapshotContainer component > renders successfully 1`] = `
|
|
|
302
302
|
</article>
|
|
303
303
|
</div>
|
|
304
304
|
</div>
|
|
305
|
+
<div
|
|
306
|
+
class="clone-dropdown css-1jpt4j3"
|
|
307
|
+
>
|
|
308
|
+
<div
|
|
309
|
+
class="undefined dropdown-wrapper"
|
|
310
|
+
>
|
|
311
|
+
<div
|
|
312
|
+
class="toggle "
|
|
313
|
+
>
|
|
314
|
+
<span>
|
|
315
|
+
<button
|
|
316
|
+
aria-label="Analyze"
|
|
317
|
+
class="on-button on-button--small on-button--primary clone-link"
|
|
318
|
+
role="button"
|
|
319
|
+
type="button"
|
|
320
|
+
>
|
|
321
|
+
Analyze
|
|
322
|
+
<i
|
|
323
|
+
class="fas fa-caret-up"
|
|
324
|
+
/>
|
|
325
|
+
<i
|
|
326
|
+
class="fas fa-caret-down"
|
|
327
|
+
/>
|
|
328
|
+
</button>
|
|
329
|
+
</span>
|
|
330
|
+
</div>
|
|
331
|
+
<div
|
|
332
|
+
class="menu collapsed"
|
|
333
|
+
role="menu"
|
|
334
|
+
>
|
|
335
|
+
<div
|
|
336
|
+
class="dataset-git-access"
|
|
337
|
+
>
|
|
338
|
+
<span>
|
|
339
|
+
<h4>
|
|
340
|
+
Analyze this dataset
|
|
341
|
+
</h4>
|
|
342
|
+
</span>
|
|
343
|
+
<p>
|
|
344
|
+
Work with OpenNeuro datasets using any of these third-party tools.
|
|
345
|
+
</p>
|
|
346
|
+
<hr />
|
|
347
|
+
<img
|
|
348
|
+
height="16"
|
|
349
|
+
src="/packages/openneuro-app/src/assets/external/brainlife.png"
|
|
350
|
+
width="16"
|
|
351
|
+
/>
|
|
352
|
+
|
|
353
|
+
<a
|
|
354
|
+
href="https://brainlife.io/openneuro/ds001032/1.0.0"
|
|
355
|
+
target="_blank"
|
|
356
|
+
>
|
|
357
|
+
View on Brainlife.io
|
|
358
|
+
</a>
|
|
359
|
+
<p>
|
|
360
|
+
Brainlife.io is a free cloud platform for secure neuroscience data analysis.
|
|
361
|
+
</p>
|
|
362
|
+
<hr />
|
|
363
|
+
<img
|
|
364
|
+
height="16"
|
|
365
|
+
src="/packages/openneuro-app/src/assets/external/nemar.png"
|
|
366
|
+
width="16"
|
|
367
|
+
/>
|
|
368
|
+
|
|
369
|
+
<a
|
|
370
|
+
href="https://nemar.org/dataexplorer/detail?dataset_id=ds001032"
|
|
371
|
+
target="_blank"
|
|
372
|
+
>
|
|
373
|
+
View on NEMAR
|
|
374
|
+
</a>
|
|
375
|
+
<p>
|
|
376
|
+
View and analyze this dataset on the NEMAR OpenNeuro portal for MEG, iEEG, and EEG data.
|
|
377
|
+
</p>
|
|
378
|
+
<hr />
|
|
379
|
+
<img
|
|
380
|
+
height="16"
|
|
381
|
+
src="/packages/openneuro-app/src/assets/external/cbrain.png"
|
|
382
|
+
width="16"
|
|
383
|
+
/>
|
|
384
|
+
|
|
385
|
+
<a
|
|
386
|
+
href="https://portal.cbrain.mcgill.ca/openneuro/ds001032/versions/1.0.0"
|
|
387
|
+
target="_blank"
|
|
388
|
+
>
|
|
389
|
+
View on CBRAIN
|
|
390
|
+
</a>
|
|
391
|
+
<p>
|
|
392
|
+
CBRAIN is a web-based distributed computing platform for collaborative neuroimaging research.
|
|
393
|
+
</p>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
305
398
|
<div
|
|
306
399
|
class="clone-dropdown"
|
|
307
400
|
>
|
|
@@ -330,6 +423,7 @@ exports[`SnapshotContainer component > renders successfully 1`] = `
|
|
|
330
423
|
</div>
|
|
331
424
|
<div
|
|
332
425
|
class="menu collapsed"
|
|
426
|
+
role="menu"
|
|
333
427
|
>
|
|
334
428
|
<div>
|
|
335
429
|
<span>
|
|
@@ -1054,6 +1148,7 @@ OCI-1131441 (R. Poldrack, PI) in any publications.
|
|
|
1054
1148
|
</div>
|
|
1055
1149
|
<div
|
|
1056
1150
|
class="menu collapsed"
|
|
1151
|
+
role="menu"
|
|
1057
1152
|
>
|
|
1058
1153
|
<div
|
|
1059
1154
|
class="version-list-dropdown"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Dropdown } from "@openneuro/components/dropdown"
|
|
3
|
+
import { Button } from "@openneuro/components/button"
|
|
4
|
+
import styled from "@emotion/styled"
|
|
5
|
+
import BrainlifeIcon from "../../../assets/external/brainlife.png"
|
|
6
|
+
import NemarIcon from "../../../assets/external/nemar.png"
|
|
7
|
+
import CbrainIcon from "../../../assets/external/cbrain.png"
|
|
8
|
+
|
|
9
|
+
export interface CloneDropdownProps {
|
|
10
|
+
datasetId: string
|
|
11
|
+
snapshotVersion: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const AnalyzeDiv = styled.div`
|
|
15
|
+
padding-right: 1em;
|
|
16
|
+
`
|
|
17
|
+
|
|
18
|
+
export const AnalyzeDropdown: React.FC<CloneDropdownProps> = (
|
|
19
|
+
{ datasetId, snapshotVersion },
|
|
20
|
+
) => {
|
|
21
|
+
const brainlifeUrl =
|
|
22
|
+
`https://brainlife.io/openneuro/${datasetId}/${snapshotVersion}`
|
|
23
|
+
const nemarUrl =
|
|
24
|
+
`https://nemar.org/dataexplorer/detail?dataset_id=${datasetId}`
|
|
25
|
+
const cbrainUrl =
|
|
26
|
+
`https://portal.cbrain.mcgill.ca/openneuro/${datasetId}/versions/${snapshotVersion}`
|
|
27
|
+
return (
|
|
28
|
+
<AnalyzeDiv className="clone-dropdown">
|
|
29
|
+
<Dropdown
|
|
30
|
+
label={
|
|
31
|
+
<Button
|
|
32
|
+
className="clone-link"
|
|
33
|
+
primary={true}
|
|
34
|
+
size="small"
|
|
35
|
+
label="Analyze"
|
|
36
|
+
>
|
|
37
|
+
<i className="fas fa-caret-up"></i>
|
|
38
|
+
<i className="fas fa-caret-down"></i>
|
|
39
|
+
</Button>
|
|
40
|
+
}
|
|
41
|
+
>
|
|
42
|
+
<div className="dataset-git-access">
|
|
43
|
+
<span>
|
|
44
|
+
<h4>Analyze this dataset</h4>
|
|
45
|
+
</span>
|
|
46
|
+
<p>
|
|
47
|
+
Work with OpenNeuro datasets using any of these third-party tools.
|
|
48
|
+
</p>
|
|
49
|
+
<hr />
|
|
50
|
+
<img
|
|
51
|
+
src={BrainlifeIcon}
|
|
52
|
+
height="16"
|
|
53
|
+
width="16"
|
|
54
|
+
/>{" "}
|
|
55
|
+
<a
|
|
56
|
+
href={brainlifeUrl}
|
|
57
|
+
target="_blank"
|
|
58
|
+
>
|
|
59
|
+
View on Brainlife.io
|
|
60
|
+
</a>
|
|
61
|
+
<p>
|
|
62
|
+
Brainlife.io is a free cloud platform for secure neuroscience data
|
|
63
|
+
analysis.
|
|
64
|
+
</p>
|
|
65
|
+
<hr />
|
|
66
|
+
<img
|
|
67
|
+
src={NemarIcon}
|
|
68
|
+
height="16"
|
|
69
|
+
width="16"
|
|
70
|
+
/>{" "}
|
|
71
|
+
<a
|
|
72
|
+
href={nemarUrl}
|
|
73
|
+
target="_blank"
|
|
74
|
+
>
|
|
75
|
+
View on NEMAR
|
|
76
|
+
</a>
|
|
77
|
+
<p>
|
|
78
|
+
View and analyze this dataset on the NEMAR OpenNeuro portal for MEG,
|
|
79
|
+
iEEG, and EEG data.
|
|
80
|
+
</p>
|
|
81
|
+
<hr />
|
|
82
|
+
<img
|
|
83
|
+
src={CbrainIcon}
|
|
84
|
+
height="16"
|
|
85
|
+
width="16"
|
|
86
|
+
/>{" "}
|
|
87
|
+
<a href={cbrainUrl} target="_blank">
|
|
88
|
+
View on CBRAIN
|
|
89
|
+
</a>
|
|
90
|
+
<p>
|
|
91
|
+
CBRAIN is a web-based distributed computing platform for
|
|
92
|
+
collaborative neuroimaging research.
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
</Dropdown>
|
|
96
|
+
</AnalyzeDiv>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react"
|
|
3
|
+
import { AnalyzeDropdown } from "../AnalyzeDropdown"
|
|
4
|
+
|
|
5
|
+
describe("AnalyzeDropdown component", () => {
|
|
6
|
+
it("opens when clicked", () => {
|
|
7
|
+
render(
|
|
8
|
+
<AnalyzeDropdown datasetId="ds000031" snapshotVersion="1.0.0" />,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
const button = screen.getByRole("button")
|
|
12
|
+
const menu = screen.getByRole("menu")
|
|
13
|
+
|
|
14
|
+
expect(button).toBeVisible()
|
|
15
|
+
expect(menu).toHaveClass("collapsed")
|
|
16
|
+
fireEvent.click(button)
|
|
17
|
+
expect(menu).toHaveClass("expanded")
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -8,14 +8,6 @@ import { Loading } from "@openneuro/components/loading"
|
|
|
8
8
|
import DatasetQueryContext from "../datalad/dataset/dataset-query-context.js"
|
|
9
9
|
import DatasetContext from "../datalad/dataset/dataset-context.js"
|
|
10
10
|
import DatasetRoutes from "./dataset-routes"
|
|
11
|
-
import FilesSubscription from "../datalad/subscriptions/files-subscription.jsx"
|
|
12
|
-
import usePermissionsSubscription from "../datalad/subscriptions/usePermissionsSubscription"
|
|
13
|
-
import useSnapshotsUpdatedSubscriptions from "../datalad/subscriptions/useSnapshotsUpdatedSubscriptions"
|
|
14
|
-
import useDatasetDeletedSubscription, {
|
|
15
|
-
datasetDeletedToast,
|
|
16
|
-
} from "../datalad/subscriptions/useDatasetDeletedSubscription.jsx"
|
|
17
|
-
import useDraftSubscription from "../datalad/subscriptions/useDraftSubscription.js"
|
|
18
|
-
|
|
19
11
|
import ErrorBoundary, {
|
|
20
12
|
ErrorBoundaryAssertionFailureException,
|
|
21
13
|
} from "../errors/errorBoundary.jsx"
|
|
@@ -41,15 +33,6 @@ export const DatasetQueryHook = ({ datasetId, draft }) => {
|
|
|
41
33
|
nextFetchPolicy: "cache-first",
|
|
42
34
|
},
|
|
43
35
|
)
|
|
44
|
-
usePermissionsSubscription([datasetId])
|
|
45
|
-
useSnapshotsUpdatedSubscriptions(datasetId)
|
|
46
|
-
useDatasetDeletedSubscription([datasetId], ({ data: subData }) => {
|
|
47
|
-
if (subData && subData.datasetDeleted === datasetId) {
|
|
48
|
-
navigate("/dashboard/datasets")
|
|
49
|
-
datasetDeletedToast(datasetId, data?.dataset?.draft?.description?.Name)
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
useDraftSubscription(datasetId)
|
|
53
36
|
|
|
54
37
|
if (error) {
|
|
55
38
|
if (error.message === "You do not have access to read this dataset.") {
|
|
@@ -94,7 +77,6 @@ export const DatasetQueryHook = ({ datasetId, draft }) => {
|
|
|
94
77
|
}}
|
|
95
78
|
>
|
|
96
79
|
<DatasetRoutes dataset={data.dataset} />
|
|
97
|
-
<FilesSubscription datasetId={datasetId} />
|
|
98
80
|
</DatasetQueryContext.Provider>
|
|
99
81
|
</ErrorBoundary>
|
|
100
82
|
</DatasetContext.Provider>
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
} from "../authentication/profile"
|
|
16
16
|
import { useCookies } from "react-cookie"
|
|
17
17
|
import { DatasetAlertDraft } from "./fragments/dataset-alert-draft"
|
|
18
|
-
import { BrainLifeButton } from "./components/BrainLifeButton"
|
|
19
18
|
import { CloneDropdown } from "./components/CloneDropdown"
|
|
20
19
|
import { DatasetGitAccess } from "./components/DatasetGitAccess"
|
|
21
20
|
import { DatasetHeader } from "./components/DatasetHeader"
|
|
@@ -141,10 +140,6 @@ const DraftContainer: React.FC<DraftContainerProps> = ({ dataset }) => {
|
|
|
141
140
|
issues={dataset.draft.issues}
|
|
142
141
|
/>
|
|
143
142
|
</ValidationBlock>
|
|
144
|
-
<BrainLifeButton
|
|
145
|
-
datasetId={datasetId}
|
|
146
|
-
onBrainlife={dataset.onBrainlife}
|
|
147
|
-
/>
|
|
148
143
|
<CloneDropdown
|
|
149
144
|
gitAccess={
|
|
150
145
|
<DatasetGitAccess
|
|
@@ -10,8 +10,7 @@ import Validation from "../validation/validation.jsx"
|
|
|
10
10
|
import { config } from "../config"
|
|
11
11
|
import DatasetCitation from "./fragments/dataset-citation.jsx"
|
|
12
12
|
import { DatasetAlertVersion } from "./fragments/dataset-alert-version"
|
|
13
|
-
|
|
14
|
-
import { BrainLifeButton } from "./components/BrainLifeButton"
|
|
13
|
+
import { AnalyzeDropdown } from "./components/AnalyzeDropdown"
|
|
15
14
|
import { CloneDropdown } from "./components/CloneDropdown"
|
|
16
15
|
import { DatasetGitAccess } from "./components/DatasetGitAccess"
|
|
17
16
|
import { DatasetHeader } from "./components/DatasetHeader"
|
|
@@ -21,7 +20,6 @@ import { MetaDataListBlock } from "./components/MetaDataListBlock"
|
|
|
21
20
|
import { ModalitiesMetaDataBlock } from "./components/ModalitiesMetaDataBlock"
|
|
22
21
|
import { ValidationBlock } from "./components/ValidationBlock"
|
|
23
22
|
import { VersionList } from "./components/VersionList"
|
|
24
|
-
import { NemarButton } from "./components/NemarButton"
|
|
25
23
|
import { Username } from "../users/username"
|
|
26
24
|
import { Loading } from "@openneuro/components/loading"
|
|
27
25
|
|
|
@@ -131,15 +129,8 @@ export const SnapshotContainer: React.FC<SnapshotContainerProps> = ({
|
|
|
131
129
|
<ValidationBlock>
|
|
132
130
|
<Validation datasetId={dataset.id} issues={snapshot.issues} />
|
|
133
131
|
</ValidationBlock>
|
|
134
|
-
<
|
|
135
|
-
datasetId={datasetId}
|
|
136
|
-
onNemar={summary?.modalities.includes("EEG") ||
|
|
137
|
-
summary?.modalities.includes("iEEG") ||
|
|
138
|
-
summary?.modalities.includes("MEG")}
|
|
139
|
-
/>
|
|
140
|
-
<BrainLifeButton
|
|
132
|
+
<AnalyzeDropdown
|
|
141
133
|
datasetId={datasetId}
|
|
142
|
-
onBrainlife={snapshot.onBrainlife}
|
|
143
134
|
snapshotVersion={snapshot.tag}
|
|
144
135
|
/>
|
|
145
136
|
<CloneDropdown
|
|
@@ -153,7 +153,6 @@ export const dataset = {
|
|
|
153
153
|
__typename: "Analytic",
|
|
154
154
|
},
|
|
155
155
|
derivatives: [],
|
|
156
|
-
onBrainlife: false,
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
export const snapshot = {
|
|
@@ -296,5 +295,4 @@ export const snapshot = {
|
|
|
296
295
|
],
|
|
297
296
|
__typename: "Snapshot",
|
|
298
297
|
hexsha: "d62fdc42e2a568a6f89e33d68d3ef343ff883e02",
|
|
299
|
-
onBrainlife: false,
|
|
300
298
|
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { render } from "@testing-library/react"
|
|
3
|
-
import { MockedProvider } from "@apollo/client/testing"
|
|
4
|
-
import FilesSubscription, {
|
|
5
|
-
deleteFilesReducer,
|
|
6
|
-
updateFilesReducer,
|
|
7
|
-
} from "../files-subscription.jsx"
|
|
8
|
-
|
|
9
|
-
describe("FilesSubscription", () => {
|
|
10
|
-
it("renders with common props", () => {
|
|
11
|
-
const { asFragment } = render(<FilesSubscription datasetId="ds001" />, {
|
|
12
|
-
wrapper: MockedProvider,
|
|
13
|
-
})
|
|
14
|
-
expect(asFragment()).toMatchSnapshot()
|
|
15
|
-
})
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
describe("deleteFilesReducer", () => {
|
|
19
|
-
let draft, filesToDelete
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
draft = {
|
|
22
|
-
a: 1,
|
|
23
|
-
files: [
|
|
24
|
-
{ filename: "deleted/file.txt" },
|
|
25
|
-
{ filename: "another_deleted_file.txt" },
|
|
26
|
-
{ filename: "path/to/file.txt" },
|
|
27
|
-
{ filename: "deleted/file/again/here" },
|
|
28
|
-
],
|
|
29
|
-
steak: "sauce",
|
|
30
|
-
}
|
|
31
|
-
filesToDelete = [
|
|
32
|
-
{ filename: "deleted" },
|
|
33
|
-
{ filename: "another_deleted_file.txt" },
|
|
34
|
-
]
|
|
35
|
-
})
|
|
36
|
-
it("removes files from draft", () => {
|
|
37
|
-
const output = deleteFilesReducer(filesToDelete, draft)
|
|
38
|
-
expect(output).toEqual({
|
|
39
|
-
a: 1,
|
|
40
|
-
files: [{ filename: "path/to/file.txt" }],
|
|
41
|
-
steak: "sauce",
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe("updateFilesReducer", () => {
|
|
47
|
-
let draft, filesToUpdate
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
draft = {
|
|
50
|
-
a: 1,
|
|
51
|
-
files: [
|
|
52
|
-
{ filename: "updated/file.txt", id: "something" },
|
|
53
|
-
{ filename: "a.txt" },
|
|
54
|
-
{ filename: "b" },
|
|
55
|
-
],
|
|
56
|
-
steak: "sauce",
|
|
57
|
-
}
|
|
58
|
-
filesToUpdate = [
|
|
59
|
-
{ filename: "updated:file.txt", id: "somethingelse" },
|
|
60
|
-
{ filename: "new:file.txt", id: "x" },
|
|
61
|
-
{ filename: "another:new:file", id: "y" },
|
|
62
|
-
]
|
|
63
|
-
})
|
|
64
|
-
it("removes files from draft", () => {
|
|
65
|
-
const output = updateFilesReducer(filesToUpdate, draft)
|
|
66
|
-
expect(output).toEqual({
|
|
67
|
-
a: 1,
|
|
68
|
-
files: [
|
|
69
|
-
{ filename: "updated/file.txt", id: "somethingelse" },
|
|
70
|
-
{ filename: "a.txt" },
|
|
71
|
-
{ filename: "b" },
|
|
72
|
-
{ filename: "new/file.txt", id: "x" },
|
|
73
|
-
{ filename: "another/new/file", id: "y" },
|
|
74
|
-
],
|
|
75
|
-
steak: "sauce",
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
})
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import PropTypes from "prop-types"
|
|
3
|
-
import { Subscription } from "@apollo/client/react/components"
|
|
4
|
-
import { gql } from "@apollo/client"
|
|
5
|
-
import { DRAFT_FILES_FRAGMENT } from "../dataset/dataset-query-fragments.js"
|
|
6
|
-
import { datasetCacheId } from "../mutations/cache-id.js"
|
|
7
|
-
// import { datasetCacheId } from '../mutations/cache-id.js'
|
|
8
|
-
|
|
9
|
-
const FILES_SUBSCRIPTION = gql`
|
|
10
|
-
subscription filesUpdated($datasetId: ID!) {
|
|
11
|
-
filesUpdated(datasetId: $datasetId) {
|
|
12
|
-
action
|
|
13
|
-
payload {
|
|
14
|
-
id
|
|
15
|
-
filename
|
|
16
|
-
size
|
|
17
|
-
directory
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
`
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Remove file with given filename from draft
|
|
25
|
-
* @param {Object[]} files - file to be deleted (see DatasetFile schema)
|
|
26
|
-
* @param {Object} draft - current draft in apollo cache
|
|
27
|
-
* @returns {Object} - updated version of draft
|
|
28
|
-
*/
|
|
29
|
-
export const deleteFilesReducer = (files, draft) => {
|
|
30
|
-
const pathMatch = files
|
|
31
|
-
.map(({ filename }) => filename.split(":").join("\\/"))
|
|
32
|
-
.join("|")
|
|
33
|
-
return {
|
|
34
|
-
...draft,
|
|
35
|
-
files: draft.files.filter((file) => !file.filename.match(pathMatch)),
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const updateFilesReducer = (files, draft) => {
|
|
40
|
-
files = files.map((file) => ({
|
|
41
|
-
...file,
|
|
42
|
-
filename: file.filename.split(":").join("/"),
|
|
43
|
-
}))
|
|
44
|
-
const newFiles = []
|
|
45
|
-
const draftFiles = [...draft.files]
|
|
46
|
-
files.forEach((file) => {
|
|
47
|
-
const updatedFileIndex = draftFiles.findIndex(
|
|
48
|
-
(draftFile) =>
|
|
49
|
-
draftFile.filename === file.filename || draftFile.id === file.id,
|
|
50
|
-
)
|
|
51
|
-
if (updatedFileIndex === -1) newFiles.push(file)
|
|
52
|
-
else draftFiles[updatedFileIndex] = file
|
|
53
|
-
})
|
|
54
|
-
return {
|
|
55
|
-
...draft,
|
|
56
|
-
files: [...draftFiles, ...newFiles],
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const draftReducer = (draft, action, payload) => {
|
|
61
|
-
switch (action) {
|
|
62
|
-
case "DELETE":
|
|
63
|
-
return deleteFilesReducer(payload, draft)
|
|
64
|
-
case "UPDATE":
|
|
65
|
-
return updateFilesReducer(payload, draft)
|
|
66
|
-
default:
|
|
67
|
-
return { ...draft }
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const FilesSubscription = ({ datasetId }) => (
|
|
72
|
-
<Subscription
|
|
73
|
-
subscription={FILES_SUBSCRIPTION}
|
|
74
|
-
variables={{ datasetId }}
|
|
75
|
-
onSubscriptionData={({ client, subscriptionData }) => {
|
|
76
|
-
const { cache } = client
|
|
77
|
-
if (subscriptionData.data.filesUpdated) {
|
|
78
|
-
const { action, payload } = subscriptionData.data.filesUpdated
|
|
79
|
-
if (action && payload) {
|
|
80
|
-
const id = datasetCacheId(datasetId)
|
|
81
|
-
const { draft } = cache.readFragment({
|
|
82
|
-
id,
|
|
83
|
-
fragment: DRAFT_FILES_FRAGMENT,
|
|
84
|
-
})
|
|
85
|
-
const updatedDraft = draftReducer(draft, action, payload)
|
|
86
|
-
cache.writeFragment({
|
|
87
|
-
id,
|
|
88
|
-
fragment: DRAFT_FILES_FRAGMENT,
|
|
89
|
-
data: {
|
|
90
|
-
__typename: "Dataset",
|
|
91
|
-
id: datasetId,
|
|
92
|
-
draft: updatedDraft,
|
|
93
|
-
},
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}}
|
|
98
|
-
/>
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
FilesSubscription.propTypes = {
|
|
102
|
-
datasetId: PropTypes.string,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export default FilesSubscription
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { gql, useSubscription } from "@apollo/client"
|
|
3
|
-
|
|
4
|
-
import { toast } from "react-toastify"
|
|
5
|
-
import ToastContent from "../../common/partials/toast-content.jsx"
|
|
6
|
-
|
|
7
|
-
const DATASET_DELETED_SUBSCRIPTION = gql`
|
|
8
|
-
subscription datasetDeleted($datasetIds: [ID!]) {
|
|
9
|
-
datasetDeleted(datasetIds: $datasetIds)
|
|
10
|
-
}
|
|
11
|
-
`
|
|
12
|
-
|
|
13
|
-
const useDatasetDeletedSubscription = (datasetIds, cb) => {
|
|
14
|
-
const result = useSubscription(DATASET_DELETED_SUBSCRIPTION, {
|
|
15
|
-
variables: { datasetIds },
|
|
16
|
-
shouldResubscribe: true,
|
|
17
|
-
})
|
|
18
|
-
cb(result)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const datasetDeletedToast = (datasetId, name = datasetId) => {
|
|
22
|
-
toast.warn(
|
|
23
|
-
<ToastContent
|
|
24
|
-
title="Deleting Dataset"
|
|
25
|
-
body={`Dataset "${name}" is being removed.`}
|
|
26
|
-
/>,
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default useDatasetDeletedSubscription
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { gql, useSubscription } from "@apollo/client"
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
DATASET_ISSUES,
|
|
5
|
-
DRAFT_FRAGMENT,
|
|
6
|
-
} from "../dataset/dataset-query-fragments.js"
|
|
7
|
-
|
|
8
|
-
const DRAFT_SUBSCRIPTION = gql`
|
|
9
|
-
subscription draftUpdated($datasetId: ID!) {
|
|
10
|
-
draftUpdated(datasetId: $datasetId) {
|
|
11
|
-
id
|
|
12
|
-
...DatasetDraft
|
|
13
|
-
...DatasetIssues
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
${DRAFT_FRAGMENT}
|
|
17
|
-
${DATASET_ISSUES}
|
|
18
|
-
`
|
|
19
|
-
|
|
20
|
-
const useDraftSubscription = (datasetId) =>
|
|
21
|
-
useSubscription(DRAFT_SUBSCRIPTION, {
|
|
22
|
-
variables: { datasetId },
|
|
23
|
-
shouldResubscribe: true,
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
export default useDraftSubscription
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { gql, useSubscription } from "@apollo/client"
|
|
2
|
-
|
|
3
|
-
import { PERMISSION_FRAGMENT } from "../dataset/dataset-query-fragments.js"
|
|
4
|
-
|
|
5
|
-
const PERMISSIONS_SUBSCRIPTION = gql`
|
|
6
|
-
subscription permissionsUpdated($datasetIds: [ID!]) {
|
|
7
|
-
permissionsUpdated(datasetIds: $datasetIds) {
|
|
8
|
-
id
|
|
9
|
-
...DatasetPermissions
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
${PERMISSION_FRAGMENT}
|
|
13
|
-
`
|
|
14
|
-
|
|
15
|
-
const usePermissionsSubscription = (datasetIds) =>
|
|
16
|
-
useSubscription(PERMISSIONS_SUBSCRIPTION, {
|
|
17
|
-
variables: { datasetIds: datasetIds || ["NULL_ID"] },
|
|
18
|
-
shouldResubscribe: true,
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
export default usePermissionsSubscription
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { gql, useSubscription } from "@apollo/client"
|
|
2
|
-
|
|
3
|
-
import { DATASET_SNAPSHOTS } from "../dataset/dataset-query-fragments.js"
|
|
4
|
-
|
|
5
|
-
export const SNAPSHOTS_UPDATED_SUBSCRIPTION = gql`
|
|
6
|
-
subscription snapshotsUpdated($datasetId: ID!) {
|
|
7
|
-
snapshotsUpdated(datasetId: $datasetId) {
|
|
8
|
-
id
|
|
9
|
-
...DatasetSnapshots
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
${DATASET_SNAPSHOTS}
|
|
13
|
-
`
|
|
14
|
-
|
|
15
|
-
const useSnapshotsUpdatedSubscription = (datasetId) =>
|
|
16
|
-
useSubscription(SNAPSHOTS_UPDATED_SUBSCRIPTION, {
|
|
17
|
-
variables: { datasetId },
|
|
18
|
-
shouldResubscribe: true,
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
export default useSnapshotsUpdatedSubscription
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { Tooltip } from "@openneuro/components/tooltip"
|
|
3
|
-
import { Button } from "@openneuro/components/button"
|
|
4
|
-
|
|
5
|
-
export interface BrainLifeButtonProps {
|
|
6
|
-
datasetId: string
|
|
7
|
-
snapshotVersion?: string
|
|
8
|
-
onBrainlife: boolean
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const BrainLifeButton: React.FC<BrainLifeButtonProps> = ({
|
|
12
|
-
datasetId,
|
|
13
|
-
snapshotVersion,
|
|
14
|
-
onBrainlife,
|
|
15
|
-
}) => {
|
|
16
|
-
const url = snapshotVersion
|
|
17
|
-
? `https://brainlife.io/openneuro/${datasetId}/${snapshotVersion}`
|
|
18
|
-
: `https://brainlife.io/openneuro/${datasetId}`
|
|
19
|
-
return (
|
|
20
|
-
<>
|
|
21
|
-
{onBrainlife && (
|
|
22
|
-
<div className="brainlife-block">
|
|
23
|
-
<Tooltip tooltip="Analyze on brainlife" flow="up">
|
|
24
|
-
<Button
|
|
25
|
-
className="brainlife-link"
|
|
26
|
-
primary={true}
|
|
27
|
-
size="small"
|
|
28
|
-
onClick={() => {
|
|
29
|
-
window.open(url, "_blank")
|
|
30
|
-
}}
|
|
31
|
-
label="brainlife.io"
|
|
32
|
-
/>
|
|
33
|
-
</Tooltip>
|
|
34
|
-
</div>
|
|
35
|
-
)}
|
|
36
|
-
</>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { Tooltip } from "@openneuro/components/tooltip"
|
|
3
|
-
import { Button } from "@openneuro/components/button"
|
|
4
|
-
|
|
5
|
-
export interface NemarButtonProps {
|
|
6
|
-
datasetId: string
|
|
7
|
-
onNemar: boolean
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const NemarButton: React.FC<NemarButtonProps> = ({
|
|
11
|
-
datasetId,
|
|
12
|
-
onNemar,
|
|
13
|
-
}) => {
|
|
14
|
-
const url = `https://nemar.org/dataexplorer/detail?dataset_id=${datasetId}`
|
|
15
|
-
return (
|
|
16
|
-
<>
|
|
17
|
-
{onNemar && (
|
|
18
|
-
<div className="brainlife-block">
|
|
19
|
-
<Tooltip
|
|
20
|
-
tooltip="View and analyze this dataset on the NEMAR OpenNeuro portal for MEG, iEEG, and EEG data"
|
|
21
|
-
flow="up"
|
|
22
|
-
wrapText={true}
|
|
23
|
-
>
|
|
24
|
-
<Button
|
|
25
|
-
className="brainlife-link"
|
|
26
|
-
primary={true}
|
|
27
|
-
size="small"
|
|
28
|
-
onClick={() => {
|
|
29
|
-
window.open(url, "_blank")
|
|
30
|
-
}}
|
|
31
|
-
label="NEMAR"
|
|
32
|
-
/>
|
|
33
|
-
</Tooltip>
|
|
34
|
-
</div>
|
|
35
|
-
)}
|
|
36
|
-
</>
|
|
37
|
-
)
|
|
38
|
-
}
|