Fix bug publish screenshot (#762)

[Preview this branch](https://cosmos-explorer-preview.azurewebsites.net/pull/762?feature.someFeatureFlagYouMightNeed=true)

The main change in this PR fixes the snapshot functionality in the Publish pane-related components. Because the code cell outputs are now rendered in their own iframes for security reasons, a single snapshot of the notebook is no longer possible: each cell output takes its own snapshot and the snapshots are collated on the main notebook snapshot.
- Move the snapshot functionality to notebook components: this removes the reference of the notebook DOM node that we must pass to the Publish pane via explorer.
- Add slice in the state and actions in notebook redux for notebook snapshot requests and result
- Add post robot message to take snapshots and receive results
- Add logic in `NotebookRenderer` to wait for all output snapshots done before taking the main one collating.
- Use `zustand` to share snapshot between Redux world and React world. This solves the issue of keeping the `PanelContainer` component generic, while being able to update its children (`PublishPanel` component) with the new snapshot.

Additional changes:
- Add `local()` in `@font-face` to check if font is already installed before downloading the font (must be done for Safari, but not Edge/Chrome)
- Add "Export output to image" menu item in notebook cell, since each cell output can take its own snapshot (which can be downloaded)
![image](https://user-images.githubusercontent.com/21954022/117454706-b5f16600-af46-11eb-8535-6bf99f3d9170.png)
This commit is contained in:
Laurent Nguyen
2021-05-11 20:24:05 +02:00
committed by GitHub
parent 4ed8fe9e7d
commit 861042c27e
24 changed files with 683 additions and 222 deletions

View File

@@ -1,5 +1,5 @@
import { ContextualMenuItemType, DirectionalHint, IconButton, IContextualMenuItem } from "@fluentui/react";
import { CellId, CellType } from "@nteract/commutable";
import { CellId, CellType, ImmutableCodeCell } from "@nteract/commutable";
import { actions, AppState, DocumentRecordProps } from "@nteract/core";
import * as selectors from "@nteract/selectors";
import { CellToolbarContext } from "@nteract/stateful-components";
@@ -10,6 +10,8 @@ import { connect } from "react-redux";
import { Dispatch } from "redux";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as cdbActions from "../NotebookComponent/actions";
import { SnapshotRequest } from "../NotebookComponent/types";
import { NotebookUtil } from "../NotebookUtil";
export interface ComponentProps {
contentRef: ContentRef;
@@ -26,12 +28,14 @@ interface DispatchProps {
clearOutputs: () => void;
deleteCell: () => void;
traceNotebookTelemetry: (action: Action, actionModifier?: string, data?: any) => void;
takeNotebookSnapshot: (payload: SnapshotRequest) => void;
}
interface StateProps {
cellType: CellType;
cellIdAbove: CellId;
cellIdBelow: CellId;
hasCodeOutput: boolean;
}
class BaseToolbar extends React.PureComponent<ComponentProps & DispatchProps & StateProps> {
@@ -58,11 +62,29 @@ class BaseToolbar extends React.PureComponent<ComponentProps & DispatchProps & S
this.props.traceNotebookTelemetry(Action.NotebooksClearOutputsFromMenu, ActionModifiers.Mark);
},
},
{
key: "Divider",
itemType: ContextualMenuItemType.Divider,
},
]);
if (this.props.hasCodeOutput) {
items.push({
key: "Export output to image",
text: "Export output to image",
onClick: () => {
this.props.takeNotebookSnapshot({
requestId: new Date().getTime().toString(),
aspectRatio: undefined,
type: "celloutput",
cellId: this.props.id,
notebookContentRef: this.props.contentRef,
downloadFilename: `celloutput-${this.props.contentRef}_${this.props.id}.png`,
});
},
});
}
items.push({
key: "Divider",
itemType: ContextualMenuItemType.Divider,
});
}
items = items.concat([
@@ -183,12 +205,13 @@ const mapDispatchToProps = (
deleteCell: () => dispatch(actions.deleteCell({ id, contentRef })),
traceNotebookTelemetry: (action: Action, actionModifier?: string, data?: any) =>
dispatch(cdbActions.traceNotebookTelemetry({ action, actionModifier, data })),
takeNotebookSnapshot: (request: SnapshotRequest) => dispatch(cdbActions.takeNotebookSnapshot(request)),
});
const makeMapStateToProps = (state: AppState, ownProps: ComponentProps): ((state: AppState) => StateProps) => {
const mapStateToProps = (state: AppState) => {
const cellType = selectors.cell.cellFromState(state, { id: ownProps.id, contentRef: ownProps.contentRef })
.cell_type;
const cell = selectors.cell.cellFromState(state, { id: ownProps.id, contentRef: ownProps.contentRef });
const cellType = cell.cell_type;
const model = selectors.model(state, { contentRef: ownProps.contentRef });
const cellOrder = selectors.notebook.cellOrder(model as RecordOf<DocumentRecordProps>);
const cellIndex = cellOrder.indexOf(ownProps.id);
@@ -199,6 +222,7 @@ const makeMapStateToProps = (state: AppState, ownProps: ComponentProps): ((state
cellType,
cellIdAbove,
cellIdBelow,
hasCodeOutput: cellType === "code" && NotebookUtil.hasCodeCellOutput(cell as ImmutableCodeCell),
};
};
return mapStateToProps;