Skip to content

Commit 0769195

Browse files
author
Michael Buchar
committed
fix(workflow-list): show shared workflows for brand new users (#429)
Closes #413
1 parent a510331 commit 0769195

File tree

9 files changed

+115
-13
lines changed

9 files changed

+115
-13
lines changed

reana-ui/src/actions.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ export function fetchWorkflows({
282282
showLoader = true,
283283
workflowIdOrName,
284284
shared = false,
285+
type,
285286
}) {
286287
return async (dispatch) => {
287288
if (showLoader) {
@@ -298,6 +299,7 @@ export function fetchWorkflows({
298299
sort,
299300
workflowIdOrName,
300301
shared,
302+
...(type ? { type } : {}),
301303
})
302304
.then((resp) =>
303305
dispatch({

reana-ui/src/client.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class Client {
134134
sort,
135135
workflowIdOrName,
136136
shared,
137+
type,
137138
} = {}) {
138139
return this._request(
139140
WORKFLOWS_URL({
@@ -145,6 +146,7 @@ class Client {
145146
shared_by: sharedBy,
146147
shared_with: sharedWith,
147148
sort,
149+
type,
148150
}),
149151
);
150152
}

reana-ui/src/pages/workflowList/WorkflowList.js

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useDispatch, useSelector } from "react-redux";
1515
import { Container, Dimmer, Dropdown, Icon, Loader } from "semantic-ui-react";
1616
import isEqual from "lodash/isEqual";
1717

18-
import { fetchWorkflows } from "~/actions";
18+
import { fetchUsersSharedWithYou, fetchWorkflows } from "~/actions";
1919
import {
2020
getConfig,
2121
getReanaToken,
@@ -25,6 +25,7 @@ import {
2525
loadingWorkflows,
2626
userHasWorkflows,
2727
getWorkflowRefresh,
28+
getUsersSharedWithYou,
2829
} from "~/selectors";
2930
import { NON_DELETED_STATUSES } from "~/config";
3031
import { Title, Pagination, Search } from "~/components";
@@ -58,6 +59,7 @@ function Workflows() {
5859
const workflows = useSelector(getWorkflows);
5960
const workflowsCount = useSelector(getWorkflowsCount);
6061
const hasUserWorkflows = useSelector(userHasWorkflows);
62+
const usersSharedWithYou = useSelector(getUsersSharedWithYou);
6163
const workflowRefresh = useSelector(getWorkflowRefresh);
6264
const loading = useSelector(loadingWorkflows);
6365
const reanaToken = useSelector(getReanaToken);
@@ -82,17 +84,27 @@ function Workflows() {
8284
const [committedSearch, setCommittedSearch] = useState(initialSearch);
8385

8486
// Owned by derived from URL
85-
const ownedByFilter = useMemo(
86-
() => (searchParams.get("shared") === "true" ? "anybody" : "you"),
87-
[searchParams],
88-
);
87+
const ownedByFilter = useMemo(() => {
88+
const sharedBy = searchParams.get("shared-by");
89+
if (sharedBy) {
90+
// Explicit owner selected, e.g. [email protected]
91+
return sharedBy;
92+
}
93+
// Fallback to "you" vs "anybody" based on `shared`
94+
return searchParams.get("shared") === "true" ? "anybody" : "you";
95+
}, [searchParams]);
8996

9097
// Shared with flag from URL
9198
const sharedWithMode = useMemo(
9299
() => searchParams.get("shared-with") === "true",
93100
[searchParams],
94101
);
95102

103+
// Load information about users who have shared workflows with you
104+
useEffect(() => {
105+
dispatch(fetchUsersSharedWithYou());
106+
}, [dispatch]);
107+
96108
const [sharedWithFilter, setSharedWithFilter] = useState(() =>
97109
searchParams.get("shared-with") === "true" ? "anybody" : undefined,
98110
);
@@ -114,6 +126,26 @@ function Workflows() {
114126
() => searchParams.get("sort") || "desc",
115127
[searchParams],
116128
);
129+
const interactiveOnlyFilter = useMemo(
130+
() => searchParams.get("open-sessions") === "true",
131+
[searchParams],
132+
);
133+
134+
const setInteractiveOnlyFilterInUrl = (on) => {
135+
setSearchParams(
136+
(prev) => {
137+
const qp = new URLSearchParams(prev);
138+
if (on) {
139+
qp.set("open-sessions", "true");
140+
} else {
141+
qp.delete("open-sessions");
142+
}
143+
qp.delete("page"); // reset pagination when filter changes
144+
return qp;
145+
},
146+
{ replace: false },
147+
);
148+
};
117149

118150
// URL writers
119151
const setStatusFilterInUrl = (nextStatus) => {
@@ -157,8 +189,19 @@ function Workflows() {
157189
(prev) => {
158190
const qp = new URLSearchParams(prev);
159191
qp.delete("shared-with"); // ensure shared-with mode is off
160-
if (next === "anybody") qp.set("shared", "true");
161-
else qp.delete("shared"); // default is "you"
192+
if (next === "anybody") {
193+
// Show anybody's workflows
194+
qp.set("shared", "true");
195+
qp.delete("shared-by");
196+
} else if (!next || next === "you") {
197+
// Back to "you": default view
198+
qp.delete("shared");
199+
qp.delete("shared-by");
200+
} else {
201+
// Specific owner selected, e.g. [email protected]
202+
qp.delete("shared");
203+
qp.set("shared-by", next);
204+
}
162205
qp.delete("page");
163206
return qp;
164207
},
@@ -172,6 +215,7 @@ function Workflows() {
172215
if (on) {
173216
qp.set("shared-with", "true");
174217
qp.delete("shared");
218+
qp.delete("shared-by");
175219
} else {
176220
qp.delete("shared-with");
177221
}
@@ -246,12 +290,19 @@ function Workflows() {
246290
// owned-by vs shared-with
247291
const inSharedWith = sharedWithMode;
248292

293+
// owned-by -> shared / shared_by mapping:
294+
// - owned_by=you -> shared=false, shared_by undefined
295+
// - owned_by=anybody -> shared=true, shared_by=null
296+
// - owned_by=<user@email> -> shared=false, shared_by=<user@email>
249297
let shared, sharedBy;
250298
if (!inSharedWith) {
251299
if (ownedByFilter === "anybody") {
252300
shared = true;
253301
sharedBy = null;
254-
} else if (ownedByFilter && ownedByFilter !== "you") {
302+
} else if (!ownedByFilter || ownedByFilter === "you") {
303+
shared = false;
304+
} else {
305+
shared = false;
255306
sharedBy = ownedByFilter;
256307
}
257308
}
@@ -282,6 +333,7 @@ function Workflows() {
282333
sharedBy,
283334
sharedWith: sharedWithParam,
284335
sort: sortDir,
336+
...(interactiveOnlyFilter ? { type: "interactive" } : {}),
285337
};
286338
}, [
287339
pagination,
@@ -293,6 +345,7 @@ function Workflows() {
293345
sharedWithFilter,
294346
sharedWithMode,
295347
sortDir,
348+
interactiveOnlyFilter,
296349
]);
297350

298351
const lastParamsRef = useRef();
@@ -365,7 +418,9 @@ function Workflows() {
365418
);
366419
}
367420

368-
if (!hasUserWorkflows) return <Welcome />;
421+
if (!hasUserWorkflows && usersSharedWithYou.length === 0) {
422+
return <Welcome />;
423+
}
369424

370425
// Flatten workflows object to array for rendering
371426
const workflowArray = Object.values(workflows || {});
@@ -395,6 +450,8 @@ function Workflows() {
395450
showDeleted={showDeletedMode}
396451
setShowDeleted={setShowDeletedInUrl}
397452
statusExplicit={statusExplicit}
453+
interactiveOnlyFilter={interactiveOnlyFilter}
454+
setInteractiveOnlyFilter={setInteractiveOnlyFilterInUrl}
398455
ownedByFilter={ownedByFilter}
399456
setOwnedByFilter={setOwnedByFilter}
400457
sharedWithFilter={sharedWithFilter}

reana-ui/src/pages/workflowList/WorkflowList.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
}
4444

4545
:global(.ui.text.container).workflow-list-container {
46-
max-width: 825px !important;
46+
max-width: 1200px !important;
4747
}
4848

4949
.paginationRow {

reana-ui/src/pages/workflowList/components/WorkflowFilters.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import WorkflowSorting from "./WorkflowSorting";
1616

1717
import styles from "./WorkflowFilters.module.scss";
1818
import WorkflowSharingFilters from "./WorkflowSharingFilter";
19+
import WorkflowSessionFilters from "./WorkflowSessionFilters";
1920

2021
export default function WorkflowFilters({
2122
statusFilter,
@@ -31,6 +32,8 @@ export default function WorkflowFilters({
3132
sharedWithMode,
3233
setSharedWithFilter,
3334
setSharedWithModeInUrl,
35+
interactiveOnlyFilter,
36+
setInteractiveOnlyFilter,
3437
}) {
3538
return (
3639
<div className={styles.container}>
@@ -42,6 +45,10 @@ export default function WorkflowFilters({
4245
setShowDeleted={setShowDeleted}
4346
statusExplicit={statusExplicit}
4447
/>
48+
<WorkflowSessionFilters
49+
enabled={interactiveOnlyFilter}
50+
filter={setInteractiveOnlyFilter}
51+
/>
4552
<WorkflowSharingFilters
4653
ownedByFilter={ownedByFilter}
4754
setOwnedByFilter={setOwnedByFilter}
@@ -71,4 +78,6 @@ WorkflowFilters.propTypes = {
7178
sharedWithFilter: PropTypes.string,
7279
sharedWithMode: PropTypes.bool,
7380
setSharedWithFilter: PropTypes.func.isRequired,
81+
interactiveOnlyFilter: PropTypes.bool.isRequired,
82+
setInteractiveOnlyFilter: PropTypes.func.isRequired,
7483
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
-*- coding: utf-8 -*-
3+
4+
This file is part of REANA.
5+
Copyright (C) 2025 CERN.
6+
7+
REANA is free software; you can redistribute it and/or modify it
8+
under the terms of the MIT License; see LICENSE file for more details.
9+
*/
10+
11+
import PropTypes from "prop-types";
12+
import { Checkbox, Grid } from "semantic-ui-react";
13+
14+
export default function WorkflowSessionFilters({ enabled, filter }) {
15+
return (
16+
<Grid.Column mobile={16} tablet={4} computer={3} className="center aligned">
17+
<Checkbox
18+
toggle
19+
label="Open sessions"
20+
checked={enabled}
21+
onChange={(_, { checked }) => filter(!!checked)}
22+
/>
23+
</Grid.Column>
24+
);
25+
}
26+
27+
WorkflowSessionFilters.propTypes = {
28+
enabled: PropTypes.bool.isRequired,
29+
filter: PropTypes.func.isRequired,
30+
};

reana-ui/src/pages/workflowList/components/WorkflowSharingFilter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export default function WorkflowSharingFilters({
121121
};
122122

123123
return (
124-
<Grid.Column mobile={16} tablet={7} computer={6}>
124+
<Grid.Column mobile={16} tablet={7} computer={4}>
125125
<div style={{ display: "flex" }}>
126126
<Dropdown
127127
fluid

reana-ui/src/pages/workflowList/components/WorkflowSharingFilter.module.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
> div[aria-atomic="true"] {
2222
max-width: 100%;
2323
white-space: nowrap;
24+
overflow: hidden;
2425
text-overflow: ellipsis;
26+
line-height: 1.2em; /* slightly taller line to prevent clipping of "y" */
2527
}
2628
}

reana-ui/src/pages/workflowList/components/WorkflowStatusFilter.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ export default function WorkflowStatusFilters({
6464
</Grid.Column>
6565
<Grid.Column
6666
mobile={16}
67-
tablet={5}
68-
computer={4}
67+
tablet={4}
68+
computer={3}
6969
className="center aligned"
7070
>
7171
<Checkbox

0 commit comments

Comments
 (0)