Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions newIDE/app/src/MainFrame/CustomToolbarButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @flow
import * as React from 'react';
import IconButton from '../UI/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import { tooltipEnterDelay } from '../UI/Tooltip';

export type ToolbarButtonConfig = {|
name: string,
icon: string,
npmScript: string,
|};

type Props = {|
name: string,
icon: string,
onClick: () => void,
|};

const iconContainerStyle = {
width: 24,
height: 24,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 16,
};

const CustomToolbarButton = ({ name, icon, onClick }: Props) => {
return (
<Tooltip title={name} placement="bottom" enterDelay={tooltipEnterDelay}>
<IconButton size="small" onClick={onClick} color="default">
<span style={iconContainerStyle}>{icon}</span>
</IconButton>
</Tooltip>
);
};

export default CustomToolbarButton;
7 changes: 7 additions & 0 deletions newIDE/app/src/MainFrame/EditorTabsPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import DrawerTopBar from '../UI/DrawerTopBar';
import { type FloatingPaneState } from './PanesContainer';
import { type CreateProjectResult } from '../Utils/UseCreateProject';
import { type OpenAskAiOptions } from '../AiGeneration/Utils';
import { type ToolbarButtonConfig } from './CustomToolbarButton';

const styles = {
container: {
Expand Down Expand Up @@ -101,6 +102,8 @@ export type EditorTabsPaneCommonProps = {|
gamesPlatformFrameTools: GamesPlatformFrameTools,
gameEditorMode: 'embedded-game' | 'instances-editor',
setGameEditorMode: ('embedded-game' | 'instances-editor') => void,
toolbarButtons: Array<ToolbarButtonConfig>,
projectPath: ?string,

// Callbacks from MainFrame
toggleProjectManager: () => void,
Expand Down Expand Up @@ -391,6 +394,8 @@ const EditorTabsPane = React.forwardRef<Props, {||}>((props, ref) => {
setGameEditorMode,
onRestartInGameEditor,
showRestartInGameEditorAfterErrorButton,
toolbarButtons,
projectPath,
} = props;

const toolbarRef = React.useRef<?ToolbarInterface>(null);
Expand Down Expand Up @@ -628,6 +633,8 @@ const EditorTabsPane = React.forwardRef<Props, {||}>((props, ref) => {
checkedOutVersionStatus={checkedOutVersionStatus}
onQuitVersionHistory={onQuitVersionHistory}
canQuitVersionHistory={!isSavingProject}
toolbarButtons={toolbarButtons}
projectPath={projectPath}
/>
<SpecificDimensionsWindowSizeProvider
innerWidth={paneWidth}
Expand Down
4 changes: 4 additions & 0 deletions newIDE/app/src/MainFrame/Preferences/PreferencesContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export type PreferencesValues = {|
aiState: {| aiRequestId: string | null |},
automaticallyUseCreditsForAiRequests: boolean,
useBackgroundSerializerForSaving: boolean,
disableNpmScriptConfirmation: boolean,
|};

/**
Expand Down Expand Up @@ -313,6 +314,7 @@ export type Preferences = {|
setUse3DEditor: (enabled: boolean) => void,
getUse3DEditor: () => boolean,
setShowBasicProfilingCounters: (enabled: boolean) => void,
setDisableNpmScriptConfirmation: (enabled: boolean) => void,
setNewProjectsDefaultStorageProviderName: (name: string) => void,
saveTutorialProgress: ({|
tutorialId: string,
Expand Down Expand Up @@ -411,6 +413,7 @@ export const initialPreferences = {
aiState: { aiRequestId: null },
automaticallyUseCreditsForAiRequests: false,
useBackgroundSerializerForSaving: false,
disableNpmScriptConfirmation: false,
},
setMultipleValues: () => {},
setLanguage: () => {},
Expand Down Expand Up @@ -472,6 +475,7 @@ export const initialPreferences = {
setUse3DEditor: (enabled: boolean) => {},
getUse3DEditor: () => false,
setShowBasicProfilingCounters: (enabled: boolean) => {},
setDisableNpmScriptConfirmation: (enabled: boolean) => {},
saveTutorialProgress: () => {},
getTutorialProgress: () => {},
setNewProjectsDefaultFolder: () => {},
Expand Down
9 changes: 9 additions & 0 deletions newIDE/app/src/MainFrame/Preferences/PreferencesDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const PreferencesDialog = ({
setShowAiAskButtonInTitleBar,
setAutomaticallyUseCreditsForAiRequests,
setShowCreateSectionByDefault,
setDisableNpmScriptConfirmation,
} = React.useContext(PreferencesContext);

const initialUse3DEditor = React.useRef<boolean>(values.use3DEditor);
Expand Down Expand Up @@ -591,6 +592,14 @@ const PreferencesDialog = ({
)}
/>
)}
{!!electron && values.disableNpmScriptConfirmation && (
<Line noMargin>
<FlatButton
label={<Trans>Re-enable npm script security warning</Trans>}
onClick={() => setDisableNpmScriptConfirmation(false)}
/>
</Line>
)}
</ColumnStackLayout>
</Column>

Expand Down
15 changes: 15 additions & 0 deletions newIDE/app/src/MainFrame/Preferences/PreferencesProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ export default class PreferencesProvider extends React.Component<Props, State> {
setShowBasicProfilingCounters: this._setShowBasicProfilingCounters.bind(
this
),
setDisableNpmScriptConfirmation: this._setDisableNpmScriptConfirmation.bind(
this
),
saveTutorialProgress: this._saveTutorialProgress.bind(this),
getTutorialProgress: this._getTutorialProgress.bind(this),
setNewProjectsDefaultFolder: this._setNewProjectsDefaultFolder.bind(this),
Expand Down Expand Up @@ -552,6 +555,18 @@ export default class PreferencesProvider extends React.Component<Props, State> {
);
}

_setDisableNpmScriptConfirmation(disableNpmScriptConfirmation: boolean) {
this.setState(
state => ({
values: {
...state.values,
disableNpmScriptConfirmation,
},
}),
() => this._persistValuesToLocalStorage(this.state)
);
}

_checkUpdates(forceDownload?: boolean) {
// Checking for updates is only done on Electron.
// Note: This could be abstracted away later if other updates mechanisms
Expand Down
83 changes: 83 additions & 0 deletions newIDE/app/src/MainFrame/Toolbar/NpmScriptConfirmDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// @flow
import * as React from 'react';
import { Trans } from '@lingui/macro';

import Dialog, { DialogPrimaryButton } from '../../UI/Dialog';
import FlatButton from '../../UI/FlatButton';
import Text from '../../UI/Text';
import InlineCheckbox from '../../UI/InlineCheckbox';

type Props = {|
open: boolean,
scriptNames: string,
onConfirm: (dontShowAgain: boolean) => void,
onDismiss: () => void,
|};

function NpmScriptConfirmDialog({
open,
scriptNames,
onConfirm,
onDismiss,
}: Props) {
const [dontShowAgain, setDontShowAgain] = React.useState(false);

const handleConfirm = React.useCallback(
() => {
onConfirm(dontShowAgain);
},
[onConfirm, dontShowAgain]
);

// Reset checkbox when dialog reopens
React.useEffect(
() => {
if (open) {
setDontShowAgain(false);
}
},
[open]
);

return (
<Dialog
dangerLevel="warning"
title={<Trans>Allow this project to run npm scripts?</Trans>}
open={open}
maxWidth="sm"
fullscreen="never-even-on-mobile"
onRequestClose={onDismiss}
onApply={handleConfirm}
actions={[
<FlatButton
key="dismiss"
keyboardFocused
label={<Trans>Don't allow</Trans>}
onClick={onDismiss}
/>,
<DialogPrimaryButton
key="confirm"
label={<Trans>I trust this project</Trans>}
onClick={handleConfirm}
primary
/>,
]}
>
<Text>
<Trans>
This project contains toolbar buttons that want to run npm scripts on
your computer ({scriptNames}). Only allow this if you created
package.json yourself or got it from a source you trust. Malicious
scripts could harm your computer or steal your data.
</Trans>
</Text>
<InlineCheckbox
label={<Trans>Don't show this warning again</Trans>}
checked={dontShowAgain}
onCheck={(e, checked) => setDontShowAgain(checked)}
/>
</Dialog>
);
}

export default NpmScriptConfirmDialog;
Loading