Skip to content

Commit b412ade

Browse files
authored
Merge branch 'develop' into fix/mobile-sidebar-color
2 parents f121fc7 + 6cb9968 commit b412ade

File tree

10 files changed

+327
-285
lines changed

10 files changed

+327
-285
lines changed

client/common/icons.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Preferences from '../images/preferences.svg';
1414
import Play from '../images/triangle-arrow-right.svg';
1515
import More from '../images/more.svg';
1616
import Code from '../images/code.svg';
17+
import Save from '../images/save.svg';
1718
import Terminal from '../images/terminal.svg';
1819

1920
import Folder from '../images/folder-padded.svg';
@@ -87,6 +88,7 @@ export const PlayIcon = withLabel(Play);
8788
export const MoreIcon = withLabel(More);
8889
export const TerminalIcon = withLabel(Terminal);
8990
export const CodeIcon = withLabel(Code);
91+
export const SaveIcon = withLabel(Save);
9092

9193
export const FolderIcon = withLabel(Folder);
9294

Lines changed: 23 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
import React from 'react';
2-
import styled from 'styled-components';
32
import PropTypes from 'prop-types';
4-
import { bindActionCreators } from 'redux';
5-
import { useDispatch, useSelector } from 'react-redux';
3+
import styled from 'styled-components';
64
import { remSize, prop } from '../../theme';
75
import IconButton from './IconButton';
8-
import { TerminalIcon, FolderIcon } from '../../common/icons';
9-
import * as IDEActions from '../../modules/IDE/actions/ide';
106

117
const BottomBarContent = styled.div`
128
padding: ${remSize(8)};
13-
display: flex;
9+
display: grid;
10+
grid-template-columns: repeat(8,1fr);
1411
1512
svg {
1613
max-height: ${remSize(32)};
17-
1814
}
1915
2016
path { fill: ${prop('primaryTextColor')} !important }
@@ -25,42 +21,28 @@ const BottomBarContent = styled.div`
2521
}
2622
`;
2723

28-
// Maybe this component shouldn't be connected, and instead just receive the `actions` prop
29-
const ActionStrip = ({ toggleExplorer }) => {
30-
const { expandConsole, collapseConsole } = bindActionCreators(IDEActions, useDispatch());
31-
const { consoleIsExpanded } = useSelector(state => state.ide);
32-
33-
const actions = [
34-
{
35-
icon: TerminalIcon, inverted: true, aria: 'Open terminal console', action: consoleIsExpanded ? collapseConsole : expandConsole
36-
},
37-
{ icon: FolderIcon, aria: 'Open files explorer', action: toggleExplorer }
38-
];
39-
40-
return (
41-
<BottomBarContent>
42-
{actions.map(({
43-
icon, aria, action, inverted
44-
}) =>
45-
(
46-
<IconButton
47-
inverted={inverted}
48-
className={inverted && 'inverted'}
49-
icon={icon}
50-
aria-label={aria}
51-
key={`bottom-bar-${aria}`}
52-
onClick={() => action()}
53-
/>))}
54-
</BottomBarContent>
55-
);
56-
};
24+
const ActionStrip = ({ actions }) => (
25+
<BottomBarContent>
26+
{actions.map(({
27+
icon, aria, action, inverted
28+
}) =>
29+
(<IconButton
30+
inverted={inverted}
31+
className={inverted && 'inverted'}
32+
icon={icon}
33+
aria-label={aria}
34+
key={`bottom-bar-${aria}`}
35+
onClick={action}
36+
/>))}
37+
</BottomBarContent>);
5738

5839
ActionStrip.propTypes = {
59-
toggleExplorer: PropTypes.func
60-
};
61-
62-
ActionStrip.defaultProps = {
63-
toggleExplorer: () => {}
40+
actions: PropTypes.arrayOf(PropTypes.shape({
41+
icon: PropTypes.any,
42+
aria: PropTypes.string.isRequired,
43+
action: PropTypes.func.isRequired,
44+
inverted: PropTypes.bool
45+
})).isRequired
6446
};
6547

6648
export default ActionStrip;

client/components/mobile/Header.jsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ const HeaderDiv = styled.div`
3535
}
3636
3737
& svg path { fill: ${textColor} !important; }
38+
39+
.editor__unsaved-changes svg {
40+
width: ${remSize(16)};
41+
padding: 0;
42+
vertical-align: top
43+
}
3844
`;
3945

4046
const IconContainer = styled.div`
@@ -71,8 +77,9 @@ const Header = ({
7177
</HeaderDiv>
7278
);
7379

80+
7481
Header.propTypes = {
75-
title: PropTypes.string,
82+
title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
7683
subtitle: PropTypes.string,
7784
leftButton: PropTypes.element,
7885
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),

client/images/save.svg

Lines changed: 3 additions & 0 deletions
Loading

client/modules/IDE/actions/project.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function getSynchedProject(currentState, responseProject) {
126126
};
127127
}
128128

129-
export function saveProject(selectedFile = null, autosave = false) {
129+
export function saveProject(selectedFile = null, autosave = false, mobile = false) {
130130
return (dispatch, getState) => {
131131
const state = getState();
132132
if (state.project.isSaving) {
@@ -185,16 +185,15 @@ export function saveProject(selectedFile = null, autosave = false) {
185185
.then((response) => {
186186
dispatch(endSavingProject());
187187
const { hasChanges, synchedProject } = getSynchedProject(getState(), response.data);
188+
189+
dispatch(setNewProject(synchedProject));
190+
dispatch(setUnsavedChanges(false));
191+
browserHistory.push(`/${response.data.user.username}/sketches/${response.data.id}`);
192+
188193
if (hasChanges) {
189-
dispatch(setNewProject(synchedProject));
190-
dispatch(setUnsavedChanges(false));
191-
browserHistory.push(`/${response.data.user.username}/sketches/${response.data.id}`);
192194
dispatch(setUnsavedChanges(true));
193-
} else {
194-
dispatch(setNewProject(synchedProject));
195-
dispatch(setUnsavedChanges(false));
196-
browserHistory.push(`/${response.data.user.username}/sketches/${response.data.id}`);
197195
}
196+
198197
dispatch(projectSaveSuccess());
199198
if (!autosave) {
200199
if (state.preferences.autosave) {
@@ -222,9 +221,9 @@ export function saveProject(selectedFile = null, autosave = false) {
222221
};
223222
}
224223

225-
export function autosaveProject() {
224+
export function autosaveProject(mobile = false) {
226225
return (dispatch, getState) => {
227-
saveProject(null, true)(dispatch, getState);
226+
saveProject(null, true, mobile)(dispatch, getState);
228227
};
229228
}
230229

client/modules/IDE/components/Editor.jsx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import { CSSLint } from 'csslint';
2727
import { HTMLHint } from 'htmlhint';
2828
import classNames from 'classnames';
2929
import { debounce } from 'lodash';
30+
import { connect } from 'react-redux';
31+
import { bindActionCreators } from 'redux';
3032
import '../../../utils/htmlmixed';
3133
import '../../../utils/p5-javascript';
3234
import '../../../utils/webGL-clike';
@@ -40,6 +42,16 @@ import beepUrl from '../../../sounds/audioAlert.mp3';
4042
import UnsavedChangesDotIcon from '../../../images/unsaved-changes-dot.svg';
4143
import RightArrowIcon from '../../../images/right-arrow.svg';
4244
import LeftArrowIcon from '../../../images/left-arrow.svg';
45+
import { getHTMLFile } from '../reducers/files';
46+
47+
import * as FileActions from '../actions/files';
48+
import * as IDEActions from '../actions/ide';
49+
import * as ProjectActions from '../actions/project';
50+
import * as EditorAccessibilityActions from '../actions/editorAccessibility';
51+
import * as PreferencesActions from '../actions/preferences';
52+
import * as UserActions from '../../User/actions';
53+
import * as ToastActions from '../actions/toast';
54+
import * as ConsoleActions from '../actions/console';
4355

4456
search(CodeMirror);
4557

@@ -413,4 +425,47 @@ Editor.defaultProps = {
413425
consoleEvents: [],
414426
};
415427

416-
export default withTranslation()(Editor);
428+
429+
function mapStateToProps(state) {
430+
return {
431+
files: state.files,
432+
file:
433+
state.files.find(file => file.isSelectedFile) ||
434+
state.files.find(file => file.name === 'sketch.js') ||
435+
state.files.find(file => file.name !== 'root'),
436+
htmlFile: getHTMLFile(state.files),
437+
ide: state.ide,
438+
preferences: state.preferences,
439+
editorAccessibility: state.editorAccessibility,
440+
user: state.user,
441+
project: state.project,
442+
toast: state.toast,
443+
console: state.console,
444+
445+
...state.preferences,
446+
...state.ide,
447+
...state.project,
448+
...state.editorAccessibility,
449+
isExpanded: state.ide.consoleIsExpanded,
450+
projectSavedTime: state.project.updatedAt
451+
};
452+
}
453+
454+
function mapDispatchToProps(dispatch) {
455+
return bindActionCreators(
456+
Object.assign(
457+
{},
458+
EditorAccessibilityActions,
459+
FileActions,
460+
ProjectActions,
461+
IDEActions,
462+
PreferencesActions,
463+
UserActions,
464+
ToastActions,
465+
ConsoleActions
466+
),
467+
dispatch
468+
);
469+
}
470+
471+
export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(Editor));

client/modules/IDE/pages/IDEView.jsx

Lines changed: 15 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -343,46 +343,7 @@ class IDEView extends React.Component {
343343
allowResize={this.props.ide.consoleIsExpanded}
344344
className="editor-preview-subpanel"
345345
>
346-
<Editor
347-
lintWarning={this.props.preferences.lintWarning}
348-
linewrap={this.props.preferences.linewrap}
349-
lintMessages={this.props.editorAccessibility.lintMessages}
350-
updateLintMessage={this.props.updateLintMessage}
351-
clearLintMessage={this.props.clearLintMessage}
352-
file={this.props.selectedFile}
353-
updateFileContent={this.props.updateFileContent}
354-
fontSize={this.props.preferences.fontSize}
355-
lineNumbers={this.props.preferences.lineNumbers}
356-
files={this.props.files}
357-
editorOptionsVisible={this.props.ide.editorOptionsVisible}
358-
showEditorOptions={this.props.showEditorOptions}
359-
closeEditorOptions={this.props.closeEditorOptions}
360-
showKeyboardShortcutModal={
361-
this.props.showKeyboardShortcutModal
362-
}
363-
setUnsavedChanges={this.props.setUnsavedChanges}
364-
isPlaying={this.props.ide.isPlaying}
365-
theme={this.props.preferences.theme}
366-
startRefreshSketch={this.props.startRefreshSketch}
367-
stopSketch={this.props.stopSketch}
368-
autorefresh={this.props.preferences.autorefresh}
369-
unsavedChanges={this.props.ide.unsavedChanges}
370-
projectSavedTime={this.props.project.updatedAt}
371-
isExpanded={this.props.ide.sidebarIsExpanded}
372-
expandSidebar={this.props.expandSidebar}
373-
collapseSidebar={this.props.collapseSidebar}
374-
isUserOwner={isUserOwner(this.props)}
375-
clearConsole={this.props.clearConsole}
376-
consoleEvents={this.props.console}
377-
showRuntimeErrorWarning={this.props.showRuntimeErrorWarning}
378-
hideRuntimeErrorWarning={this.props.hideRuntimeErrorWarning}
379-
runtimeErrorWarningVisible={
380-
this.props.ide.runtimeErrorWarningVisible
381-
}
382-
provideController={(ctl) => {
383-
this.cmController = ctl;
384-
}}
385-
/>
346+
<Editor provideController={(ctl) => { this.cmController = ctl; }} />
386347
<Console />
387348
</SplitPane>
388349
<section className="preview-frame-holder">
@@ -533,31 +494,25 @@ IDEView.propTypes = {
533494
}).isRequired,
534495
saveProject: PropTypes.func.isRequired,
535496
ide: PropTypes.shape({
536-
isPlaying: PropTypes.bool.isRequired,
537-
isAccessibleOutputPlaying: PropTypes.bool.isRequired,
538-
consoleEvent: PropTypes.array, // eslint-disable-line
539-
modalIsVisible: PropTypes.bool.isRequired,
540-
sidebarIsExpanded: PropTypes.bool.isRequired,
541-
consoleIsExpanded: PropTypes.bool.isRequired,
542-
preferencesIsVisible: PropTypes.bool.isRequired,
543-
projectOptionsVisible: PropTypes.bool.isRequired,
544-
newFolderModalVisible: PropTypes.bool.isRequired,
497+
errorType: PropTypes.string,
498+
keyboardShortcutVisible: PropTypes.bool.isRequired,
545499
shareModalVisible: PropTypes.bool.isRequired,
546500
shareModalProjectId: PropTypes.string.isRequired,
547501
shareModalProjectName: PropTypes.string.isRequired,
548502
shareModalProjectUsername: PropTypes.string.isRequired,
549-
editorOptionsVisible: PropTypes.bool.isRequired,
550-
keyboardShortcutVisible: PropTypes.bool.isRequired,
551-
unsavedChanges: PropTypes.bool.isRequired,
552-
infiniteLoop: PropTypes.bool.isRequired,
553-
previewIsRefreshing: PropTypes.bool.isRequired,
554-
infiniteLoopMessage: PropTypes.string.isRequired,
555-
projectSavedTime: PropTypes.string,
556503
previousPath: PropTypes.string.isRequired,
557-
justOpenedProject: PropTypes.bool.isRequired,
558-
errorType: PropTypes.string,
559-
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
504+
previewIsRefreshing: PropTypes.bool.isRequired,
505+
isPlaying: PropTypes.bool.isRequired,
506+
isAccessibleOutputPlaying: PropTypes.bool.isRequired,
507+
projectOptionsVisible: PropTypes.bool.isRequired,
508+
preferencesIsVisible: PropTypes.bool.isRequired,
509+
modalIsVisible: PropTypes.bool.isRequired,
560510
uploadFileModalVisible: PropTypes.bool.isRequired,
511+
newFolderModalVisible: PropTypes.bool.isRequired,
512+
justOpenedProject: PropTypes.bool.isRequired,
513+
sidebarIsExpanded: PropTypes.bool.isRequired,
514+
consoleIsExpanded: PropTypes.bool.isRequired,
515+
unsavedChanges: PropTypes.bool.isRequired,
561516
}).isRequired,
562517
stopSketch: PropTypes.func.isRequired,
563518
project: PropTypes.shape({
@@ -572,11 +527,9 @@ IDEView.propTypes = {
572527
editorAccessibility: PropTypes.shape({
573528
lintMessages: PropTypes.array.isRequired, // eslint-disable-line
574529
}).isRequired,
575-
updateLintMessage: PropTypes.func.isRequired,
576-
clearLintMessage: PropTypes.func.isRequired,
577530
preferences: PropTypes.shape({
578-
fontSize: PropTypes.number.isRequired,
579531
autosave: PropTypes.bool.isRequired,
532+
fontSize: PropTypes.number.isRequired,
580533
linewrap: PropTypes.bool.isRequired,
581534
lineNumbers: PropTypes.bool.isRequired,
582535
lintWarning: PropTypes.bool.isRequired,
@@ -602,7 +555,6 @@ IDEView.propTypes = {
602555
name: PropTypes.string.isRequired,
603556
content: PropTypes.string.isRequired,
604557
})).isRequired,
605-
updateFileContent: PropTypes.func.isRequired,
606558
selectedFile: PropTypes.shape({
607559
id: PropTypes.string.isRequired,
608560
content: PropTypes.string.isRequired,
@@ -630,9 +582,6 @@ IDEView.propTypes = {
630582
closeNewFileModal: PropTypes.func.isRequired,
631583
createFolder: PropTypes.func.isRequired,
632584
closeShareModal: PropTypes.func.isRequired,
633-
showEditorOptions: PropTypes.func.isRequired,
634-
closeEditorOptions: PropTypes.func.isRequired,
635-
showKeyboardShortcutModal: PropTypes.func.isRequired,
636585
closeKeyboardShortcutModal: PropTypes.func.isRequired,
637586
toast: PropTypes.shape({
638587
isVisible: PropTypes.bool.isRequired,
@@ -642,22 +591,14 @@ IDEView.propTypes = {
642591
setRouteLeaveHook: PropTypes.func,
643592
}).isRequired,
644593
route: PropTypes.oneOfType([PropTypes.object, PropTypes.element]).isRequired,
645-
setUnsavedChanges: PropTypes.func.isRequired,
646594
setTheme: PropTypes.func.isRequired,
647595
endSketchRefresh: PropTypes.func.isRequired,
648-
startRefreshSketch: PropTypes.func.isRequired,
649596
setBlobUrl: PropTypes.func.isRequired,
650597
setPreviousPath: PropTypes.func.isRequired,
651-
console: PropTypes.arrayOf(PropTypes.shape({
652-
method: PropTypes.string.isRequired,
653-
args: PropTypes.arrayOf(PropTypes.string),
654-
})).isRequired,
655598
clearConsole: PropTypes.func.isRequired,
656599
showErrorModal: PropTypes.func.isRequired,
657600
hideErrorModal: PropTypes.func.isRequired,
658601
clearPersistedState: PropTypes.func.isRequired,
659-
showRuntimeErrorWarning: PropTypes.func.isRequired,
660-
hideRuntimeErrorWarning: PropTypes.func.isRequired,
661602
startSketch: PropTypes.func.isRequired,
662603
openUploadFileModal: PropTypes.func.isRequired,
663604
closeUploadFileModal: PropTypes.func.isRequired,

0 commit comments

Comments
 (0)