Skip to content

Commit

Permalink
Add download modal to dataset view actions (#6283)
Browse files Browse the repository at this point in the history
* some download modal refactoring

* add python modal visibility action

* add pythonClientModal to dataset actions

* prettyfication

* make modal lazy

* make share modal lazy

* Update frontend/javascripts/oxalis/view/action-bar/view_dataset_actions_view.tsx

* also suggest open_remote in export dialog; update changelog

* small visual tweaks

Co-authored-by: Philipp Otto <[email protected]>
Co-authored-by: Philipp Otto <[email protected]>
  • Loading branch information
3 people authored Jun 21, 2022
1 parent 7710e36 commit 8cac67e
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Added
- Added a image data download speed indicator to the statusbar. On hover a tooltip is shown that show the total amount of downloaded shard data. [#6269](https://github.com/scalableminds/webknossos/pull/6269)
- Added a warning for when the resolution in the XY viewport on z=1-downsampled datasets becomes too low, explaining the problem and how to mitigate it. [#6255](https://github.com/scalableminds/webknossos/pull/6255)
- Provide a UI to download/export a dataset in view-mode. The UI explains how to access the data with the python library. [#6283](https://github.com/scalableminds/webknossos/pull/6283)
- Added the possibility to view and download older versions of read-only annotations. [#6274](https://github.com/scalableminds/webknossos/pull/6274)

### Changed
Expand Down
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,116 @@
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>"}"):
# Download the dataset.
dataset = wk.Dataset.download(
url="${window.location.origin}",
dataset="${dataset.name}"
organization_id="${dataset.owningOrganization}",
)
# Alternatively, directly open the dataset. Image data will be
# streamed when being accessed.
remote_dataset = wk.Dataset.open_remote(
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={800}
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 dataset 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,21 @@
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,
DownloadOutlined,
} 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 +27,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 +53,13 @@ export default function ViewDatasetActionsView(props: Props) {
Share
</Menu.Item>
{screenshotMenuItem}
<Menu.Item
key="python-client-button"
onClick={() => Store.dispatch(setPythonClientModalVisibilityAction(true))}
>
<DownloadOutlined />
Download
</Menu.Item>
{props.layoutMenu}
</Menu>
);
Expand All @@ -39,7 +69,8 @@ export default function ViewDatasetActionsView(props: Props) {
marginLeft: 10,
}}
>
{modal}
{shareDatasetModal}
{pythonClientModal}
<Dropdown overlay={overlayMenu} trigger={["click"]}>
<ButtonComponent
style={{
Expand Down

0 comments on commit 8cac67e

Please sign in to comment.