Skip to content

Commit

Permalink
fix(ui): fix workflow sharing interface after UI changes (reanahub#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdonadoni authored and tiborsimko committed Sep 4, 2024
1 parent 089853f commit 83eee02
Show file tree
Hide file tree
Showing 13 changed files with 441 additions and 682 deletions.
143 changes: 34 additions & 109 deletions reana-ui/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,28 @@ import client, {
INTERACTIVE_SESSIONS_OPEN_URL,
USER_INFO_URL,
USER_SIGNOUT_URL,
USERS_SHARED_WITH_YOU_URL,
USERS_YOU_SHARED_WITH_URL,
WORKFLOW_FILES_URL,
WORKFLOW_LOGS_URL,
WORKFLOW_RETENTION_RULES_URL,
WORKFLOW_SET_STATUS_URL,
WORKFLOW_SHARE_STATUS_URL,
WORKFLOW_SPECIFICATION_URL,
} from "~/client";
import {
parseWorkflows,
parseWorkflowRetentionRules,
parseLogs,
parseFiles,
formatSearch,
} from "~/util";
import {
getConfig,
getWorkflow,
getWorkflowLogs,
getWorkflowSpecification,
} from "~/selectors";
import {
formatSearch,
parseFiles,
parseLogs,
parseWorkflowRetentionRules,
parseWorkflows,
} from "~/util";

export const ERROR = "Error";
export const NOTIFICATION = "Notification";
Expand Down Expand Up @@ -96,26 +99,11 @@ export const WORKFLOW_SHARE_STATUS_FETCH = "Fetch workflow share status";
export const WORKFLOW_SHARE_STATUS_RECEIVED = "Workflow share status received";
export const WORKFLOW_SHARE_STATUS_FETCH_ERROR =
"Fetch workflow share status error";
export const WORKFLOW_SHARE_INIT = "Initialise workflow sharing";
export const WORKFLOW_SHARED_SUCCESSFULLY = "Workflow shared successfully";
export const WORKFLOW_SHARED_ERROR = "Workflow shared error";
export const WORKFLOW_SHARE_FINISH = "Finish workflow sharing";
export const WORKFLOW_UNSHARE_INIT = "Initialise workflow unsharing";
export const WORKFLOW_UNSHARED = "Workflow unshared";
export const WORKFLOW_UNSHARE_ERROR = "Workflow unshare error";

export const USERS_SHARED_WITH_YOU_FETCH =
"Fetch users who shared workflows with you";

export const USERS_SHARED_WITH_YOU_RECEIVED =
"Users who shared workflows with you received";
export const USERS_SHARED_WITH_YOU_FETCH_ERROR =
"Fetch users who shared workflows with you error";
export const USERS_YOU_SHARED_WITH_FETCH =
"Fetch users you shared workflows with";
export const USERS_YOU_SHARED_WITH_RECEIVED =
"Users you shared workflows with received";
export const USERS_YOU_SHARED_WITH_FETCH_ERROR =
"Fetch users you shared workflows with error";

export function errorActionCreator(error, name) {
const { status, data } = error?.response;
Expand Down Expand Up @@ -288,12 +276,12 @@ export function fetchWorkflows({
pagination,
search,
status,
ownedBy,
sharedBy,
sharedWith,
sort,
showLoader = true,
workflowIdOrName,
includeShared = false,
shared = false,
}) {
return async (dispatch) => {
if (showLoader) {
Expand All @@ -305,20 +293,20 @@ export function fetchWorkflows({
pagination,
search: formatSearch(search),
status,
ownedBy,
sharedBy,
sharedWith,
sort,
workflowIdOrName,
includeShared,
shared,
})
.then((resp) => {
.then((resp) =>
dispatch({
type: WORKFLOWS_RECEIVED,
workflows: parseWorkflows(resp.data.items),
total: resp.data.total,
userHasWorkflows: resp.data.user_has_workflows,
});
})
}),
)
.catch((err) => {
dispatch(errorActionCreator(err, USER_INFO_URL));
dispatch({ type: WORKFLOWS_FETCH_ERROR });
Expand All @@ -338,7 +326,7 @@ export function fetchWorkflow(id, { refetch = false, showLoader = true } = {}) {
fetchWorkflows({
workflowIdOrName: id,
showLoader,
includeShared: true,
shared: true,
}),
);
}
Expand Down Expand Up @@ -567,38 +555,34 @@ export function closeShareWorkflowModal() {

export function fetchUsersSharedWithYou() {
return async (dispatch) => {
dispatch({ type: USERS_SHARED_WITH_YOU_FETCH });

return await client
.getUsersSharedWithYou()
.then((resp) => {
dispatch({
type: USERS_SHARED_WITH_YOU_RECEIVED,
users_shared_with_you: resp.data.users_shared_with_you,
usersSharedYouWith: resp.data.users_shared_with_you,
});
return resp;
})
.catch((err) => {
dispatch(errorActionCreator(err, USERS_SHARED_WITH_YOU_FETCH_ERROR));
dispatch(errorActionCreator(err, USERS_SHARED_WITH_YOU_URL));
});
};
}

export function fetchUsersYouSharedWith() {
return async (dispatch) => {
dispatch({ type: USERS_YOU_SHARED_WITH_FETCH });

return await client
.getUsersYouSharedWith()
.then((resp) => {
dispatch({
type: USERS_YOU_SHARED_WITH_RECEIVED,
users_you_shared_with: resp.data.users_you_shared_with,
usersYouSharedWith: resp.data.users_you_shared_with,
});
return resp;
})
.catch((err) => {
dispatch(errorActionCreator(err, USERS_YOU_SHARED_WITH_FETCH_ERROR));
dispatch(errorActionCreator(err, USERS_YOU_SHARED_WITH_URL));
});
};
}
Expand All @@ -609,83 +593,24 @@ export function fetchWorkflowShareStatus(id) {
return await client
.getWorkflowShareStatus(id)
.then((resp) => {
// convert to camel-case
const sharedWith = [];
for (const share of resp.data.shared_with) {
sharedWith.push({
userEmail: share.user_email,
validUntil: share.valid_until,
});
}
dispatch({
type: WORKFLOW_SHARE_STATUS_RECEIVED,
id,
workflow_share_status: resp.data.shared_with,
sharedWith,
});
return resp;
})
.catch((err) => {
dispatch(errorActionCreator(err, WORKFLOW_SHARE_STATUS_FETCH_ERROR));
});
};
}

export function shareWorkflow(
id,
user_id,
user_emails_to_share_with,
valid_until,
) {
return async (dispatch) => {
dispatch({ type: WORKFLOW_SHARE_INIT });

const users_shared_with = [];
const users_not_shared_with = [];

for (const user_email_to_share_with of user_emails_to_share_with) {
await client
.shareWorkflow(id, {
user_id,
user_email_to_share_with,
valid_until,
})
.then(() => {
users_shared_with.push(user_email_to_share_with);
})
.catch((err) => {
const error_message = err.response.data.message;
users_not_shared_with.push({
user_email_to_share_with,
error_message,
});
});

if (users_shared_with.length > 0) {
dispatch({
type: WORKFLOW_SHARED_SUCCESSFULLY,
users_shared_with,
});
}

if (users_not_shared_with.length > 0) {
dispatch({
type: WORKFLOW_SHARED_ERROR,
users_not_shared_with,
});
}
}

dispatch({ type: WORKFLOW_SHARE_FINISH });
};
}

export function unshareWorkflow(id, user_id, user_email_to_unshare_with) {
return async (dispatch) => {
dispatch({ type: WORKFLOW_UNSHARE_INIT });

return await client
.unshareWorkflow(id, {
user_id,
user_email_to_unshare_with,
})
.then(() => {
dispatch({ type: WORKFLOW_UNSHARED, user_email_to_unshare_with });
})
.catch((err) => {
const error_message = err.response.data.message;
dispatch({ type: WORKFLOW_UNSHARE_ERROR, error_message });
dispatch({ type: WORKFLOW_SHARE_STATUS_FETCH_ERROR });
dispatch(errorActionCreator(err, WORKFLOW_SHARE_STATUS_URL(id)));
});
};
}
25 changes: 10 additions & 15 deletions reana-ui/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,28 +127,20 @@ class Client {
pagination,
search,
status,
ownedBy,
sharedBy,
sharedWith,
sort,
workflowIdOrName,
includeShared = false,
shared,
} = {}) {
let shared = false;
if (ownedBy === "anybody" || includeShared) {
ownedBy = undefined;
shared = true;
} else if (ownedBy === "you") {
ownedBy = undefined;
}

return this._request(
WORKFLOWS_URL({
...(pagination ?? {}),
workflow_id_or_name: workflowIdOrName,
search,
status,
shared,
shared_by: ownedBy,
shared_by: sharedBy,
shared_with: sharedWith,
sort,
}),
Expand Down Expand Up @@ -238,16 +230,19 @@ class Client {
return this._request(WORKFLOW_SHARE_STATUS_URL(id));
}

shareWorkflow(id, data) {
shareWorkflow(id, { userEmailToShareWith, validUntil }) {
return this._request(WORKFLOW_SHARE_URL(id), {
data,
data: {
user_email_to_share_with: userEmailToShareWith,
valid_until: validUntil,
},
method: "post",
});
}

unshareWorkflow(id, data) {
unshareWorkflow(id, { userEmailToUnshareWith }) {
return this._request(WORKFLOW_UNSHARE_URL(id), {
data,
data: { user_email_to_unshare_with: userEmailToUnshareWith },
method: "post",
});
}
Expand Down
33 changes: 9 additions & 24 deletions reana-ui/src/components/WorkflowActionsPopup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,32 @@
under the terms of the MIT License; see LICENSE file for more details.
*/

import { useState } from "react";
import PropTypes from "prop-types";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Icon, Menu, Popup } from "semantic-ui-react";
import { useDispatch } from "react-redux";

import { workflowShape } from "~/props";
import {
deleteWorkflow,
closeInteractiveSession,
deleteWorkflow,
openDeleteWorkflowModal,
openStopWorkflowModal,
openInteractiveSessionModal,
openShareWorkflowModal,
openStopWorkflowModal,
} from "~/actions";
import { workflowShape } from "~/props";
import { getUserEmail } from "~/selectors";

import { JupyterNotebookIcon } from "~/components";

import styles from "./WorkflowActionsPopup.module.scss";

const JupyterIcon = <JupyterNotebookIcon className={styles["jupyter-icon"]} />;

export default function WorkflowActionsPopup({
workflow,
className,
insideClickableElement,
}) {
export default function WorkflowActionsPopup({ workflow, className }) {
const dispatch = useDispatch();
const [open, setOpen] = useState(false);
const userEmail = useSelector(getUserEmail);
const { id, size, status, session_status: sessionStatus } = workflow;
const isDeleted = status === "deleted";
const isDeletedUsingWorkspace = isDeleted && size.raw > 0;
Expand Down Expand Up @@ -63,7 +61,6 @@ export default function WorkflowActionsPopup({
onClick: (e) => {
dispatch(openShareWorkflowModal(workflow));
setOpen(false);
e.stopPropagation();
},
});

Expand Down Expand Up @@ -115,20 +112,9 @@ export default function WorkflowActionsPopup({
});
}

if (workflow.owner_email !== "-") {
return (
<div
className={className || styles.container}
style={
insideClickableElement ? { cursor: "pointer" } : { cursor: "default" }
}
/>
);
}

return (
<div className={className}>
{menuItems.length > 0 && (
{workflow.ownerEmail === userEmail && menuItems.length > 0 && (
<Popup
basic
trigger={
Expand Down Expand Up @@ -160,5 +146,4 @@ WorkflowActionsPopup.defaultProps = {
WorkflowActionsPopup.propTypes = {
workflow: workflowShape.isRequired,
className: PropTypes.string,
insideClickableElement: PropTypes.bool,
};
Loading

0 comments on commit 83eee02

Please sign in to comment.