Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add download modal to dataset view actions #6283

Merged
merged 13 commits into from
Jun 21, 2022
Merged
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/default_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ const defaultState: OxalisState = {
showDropzoneModal: false,
showVersionRestore: false,
showDownloadModal: false,
showPythonClientModal: false,
showShareModal: false,
storedLayouts: {},
isImportingMesh: false,
Expand Down
15 changes: 11 additions & 4 deletions frontend/javascripts/oxalis/model/actions/ui_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ type SetThemeAction = {
type: "SET_THEME";
value: Theme;
};

type SetDownloadModalVisibilityAction = {
type: "SET_DOWNLOAD_MODAL_VISIBILITY";
visible: boolean;
};

type SetPythonClientModalVisibilityAction = {
type: "SET_PYTHON_MODAL_VISIBILITY";
visible: boolean;
};
type SetShareModalVisibilityAction = {
type: "SET_SHARE_MODAL_VISIBILITY";
visible: boolean;
Expand All @@ -68,6 +70,7 @@ export type UiAction =
| CycleToolAction
| SetThemeAction
| SetDownloadModalVisibilityAction
| SetPythonClientModalVisibilityAction
| SetShareModalVisibilityAction
| SetBusyBlockingInfoAction;
export const setDropzoneModalVisibilityAction = (
Expand Down Expand Up @@ -117,14 +120,18 @@ export const setThemeAction = (value: Theme): SetThemeAction => ({
type: "SET_THEME",
value,
});

export const setDownloadModalVisibilityAction = (
visible: boolean,
): SetDownloadModalVisibilityAction => ({
type: "SET_DOWNLOAD_MODAL_VISIBILITY",
visible,
});

export const setPythonClientModalVisibilityAction = (
visible: boolean,
): SetPythonClientModalVisibilityAction => ({
type: "SET_PYTHON_MODAL_VISIBILITY",
visible,
});
export const setShareModalVisibilityAction = (visible: boolean): SetShareModalVisibilityAction => ({
type: "SET_SHARE_MODAL_VISIBILITY",
visible,
Expand Down
4 changes: 4 additions & 0 deletions frontend/javascripts/oxalis/model/reducers/ui_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ function UiReducer(state: OxalisState, action: Action): OxalisState {
return updateKey(state, "uiInformation", { showDownloadModal: action.visible });
}

case "SET_PYTHON_MODAL_VISIBILITY": {
return updateKey(state, "uiInformation", { showPythonClientModal: action.visible });
}

case "SET_SHARE_MODAL_VISIBILITY": {
return updateKey(state, "uiInformation", {
showShareModal: action.visible,
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ type UiInformation = {
readonly showDropzoneModal: boolean;
readonly showVersionRestore: boolean;
readonly showDownloadModal: boolean;
readonly showPythonClientModal: boolean;
readonly showShareModal: boolean;
readonly activeTool: AnnotationTool;
readonly storedLayouts: Record<string, any>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ type Props = {
hasVolumeFallback: boolean;
};

function Hint({ children, style }: { children: React.ReactNode; style: React.CSSProperties }) {
export function Hint({
children,
style,
}: {
children: React.ReactNode;
style: React.CSSProperties;
}) {
return (
<div style={{ ...style, fontSize: 12, color: "var(--ant-text-secondary)" }}>{children}</div>
);
Expand All @@ -45,7 +51,7 @@ export async function copyToClipboard(code: string) {
Toast.success("Snippet copied to clipboard.");
}

function MoreInfoHint() {
export function MoreInfoHint() {
return (
<Hint
style={{
Expand All @@ -65,7 +71,7 @@ function MoreInfoHint() {
);
}

function CopyableCodeSnippet({ code, onCopy }: { code: string; onCopy?: () => void }) {
export function CopyableCodeSnippet({ code, onCopy }: { code: string; onCopy?: () => void }) {
return (
<pre>
<Button
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Divider, Modal, Row, Typography } from "antd";
import React from "react";
import { useFetch, makeComponentLazy } from "libs/react_helpers";
import Toast from "libs/toast";
import messages from "messages";
import { getAuthToken } from "admin/admin_rest_api";
import { useSelector } from "react-redux";
import type { OxalisState } from "oxalis/store";
import { CopyableCodeSnippet, MoreInfoHint } from "./download_modal_view";
const { Paragraph, Text } = Typography;
type Props = {
isVisible: boolean;
onClose: () => void;
};

function _PythonClientModalView(props: Props): JSX.Element {
const { isVisible, onClose } = props;
const activeUser = useSelector((state: OxalisState) => state.activeUser);
const dataset = useSelector((state: OxalisState) => state.dataset);

const maybeShowWarning = () => (
<Row>
<Text
style={{
margin: "0 6px 12px",
}}
type="warning"
>
{activeUser != null
? messages["annotation.python_do_not_share"]
: messages["annotation.register_for_token"]}
</Text>
</Row>
);

const authToken = useFetch(
async () => {
if (activeUser != null) {
return getAuthToken();
}
return null;
},
"loading...",
[activeUser],
);
const wkInitSnippet = `import webknossos as wk

with wk.webknossos_context(token="${authToken || "<insert token here>"}"):
dataset = wk.Dataset.download(
url="${window.location.origin}",
dataset="${dataset.name}"
organization_id="${dataset.owningOrganization}",
)
`;

const alertTokenIsPrivate = () => {
Toast.warning(
"The clipboard contains private data. Do not share this information with anyone you do not trust!",
);
};

return (
<Modal
title="Python Client"
visible={isVisible}
width={600}
footer={null}
onCancel={onClose}
style={{ overflow: "visible" }}
>
<Row>
<Text
style={{
margin: "0 6px 12px",
}}
>
The following code snippets are suggestions to get you started quickly with the{" "}
<a href="https://docs.webknossos.org/webknossos-py/" target="_blank" rel="noreferrer">
webKnossos Python API
</a>
. To download and use this annotation in your Python project, simply copy and paste the
code snippets to your script.
</Text>
</Row>
<Divider
style={{
margin: "18px 0",
}}
>
Code Snippets
</Divider>
{maybeShowWarning()}
<Paragraph>
<CopyableCodeSnippet code="pip install webknossos" />
<CopyableCodeSnippet code={wkInitSnippet} onCopy={alertTokenIsPrivate} />
</Paragraph>
<Divider
style={{
margin: "18px 0",
}}
/>
<MoreInfoHint />
</Modal>
);
}

const PythonClientModalView = makeComponentLazy(_PythonClientModalView);
export default PythonClientModalView;
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@ import { CopyOutlined } from "@ant-design/icons";
import { Modal, Input, Button, Row, Col } from "antd";
import { useSelector } from "react-redux";
import React from "react";
import { makeComponentLazy } from "libs/react_helpers";
import messages from "messages";
import { OxalisState } from "oxalis/store";
import { useDatasetSharingToken, getUrl, copyUrlToClipboard } from "./share_modal_view";

const sharingActiveNode = false;

type Props = {
isVisible: boolean;
onOk: () => any;
};

export default function ShareViewDatasetModalView(props: Props) {
const { onOk } = props;
function _ShareViewDatasetModalView(props: Props) {
const { isVisible, onOk } = props;
const dataset = useSelector((state: OxalisState) => state.dataset);
const isShareModalOpen = useSelector((state: OxalisState) => state.uiInformation.showShareModal);
const sharingToken = useDatasetSharingToken(dataset);
const url = getUrl(sharingToken, !dataset.isPublic);
return (
<Modal
title="Share this Dataset"
visible={isShareModalOpen}
visible={isVisible}
width={800}
okText="Ok"
onOk={onOk}
Expand Down Expand Up @@ -71,3 +72,6 @@ export default function ShareViewDatasetModalView(props: Props) {
</Modal>
);
}

const ShareViewDatasetModalView = makeComponentLazy(_ShareViewDatasetModalView);
export default ShareViewDatasetModalView;
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ type StateProps = {
task: Task | null | undefined;
activeUser: APIUser | null | undefined;
hasTracing: boolean;
isShareModalOpen: boolean;
isDownloadModalOpen: boolean;
isShareModalOpen: boolean;
busyBlockingInfo: BusyBlockingInfo;
};
type Props = OwnProps & StateProps;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from "react";
import { useSelector } from "react-redux";
import { Dropdown, Menu } from "antd";
import { ShareAltOutlined, DownOutlined, CameraOutlined } from "@ant-design/icons";
import { ShareAltOutlined, DownOutlined, CameraOutlined, CodeOutlined } from "@ant-design/icons";
import ButtonComponent from "oxalis/view/components/button_component";
import ShareViewDatasetModalView from "oxalis/view/action-bar/share_view_dataset_modal_view";
import { downloadScreenshot } from "oxalis/view/rendering_utils";
import { setShareModalVisibilityAction } from "oxalis/model/actions/ui_actions";
import Store from "oxalis/store";
import {
setPythonClientModalVisibilityAction,
setShareModalVisibilityAction,
} from "oxalis/model/actions/ui_actions";
import Store, { OxalisState } from "oxalis/store";
import PythonClientModalView from "./python_client_modal_view";

type Props = {
layoutMenu: React.ReactNode;
Expand All @@ -17,8 +22,21 @@ export const screenshotMenuItem = (
</Menu.Item>
);
export default function ViewDatasetActionsView(props: Props) {
const modal = (
<ShareViewDatasetModalView onOk={() => Store.dispatch(setShareModalVisibilityAction(false))} />
const isShareModalOpen = useSelector((state: OxalisState) => state.uiInformation.showShareModal);
const isPythonClientModalOpen = useSelector(
(state: OxalisState) => state.uiInformation.showPythonClientModal,
);
const shareDatasetModal = (
<ShareViewDatasetModalView
isVisible={isShareModalOpen}
onOk={() => Store.dispatch(setShareModalVisibilityAction(false))}
/>
);
const pythonClientModal = (
<PythonClientModalView
isVisible={isPythonClientModalOpen}
onClose={() => Store.dispatch(setPythonClientModalVisibilityAction(false))}
/>
);
const overlayMenu = (
<Menu>
Expand All @@ -30,6 +48,13 @@ export default function ViewDatasetActionsView(props: Props) {
Share
</Menu.Item>
{screenshotMenuItem}
<Menu.Item
key="python-client-button"
onClick={() => Store.dispatch(setPythonClientModalVisibilityAction(true))}
>
<CodeOutlined />
Python Client
philippotto marked this conversation as resolved.
Show resolved Hide resolved
</Menu.Item>
{props.layoutMenu}
</Menu>
);
Expand All @@ -39,7 +64,8 @@ export default function ViewDatasetActionsView(props: Props) {
marginLeft: 10,
}}
>
{modal}
{shareDatasetModal}
{pythonClientModal}
<Dropdown overlay={overlayMenu} trigger={["click"]}>
<ButtonComponent
style={{
Expand Down