Skip to content

Commit ff658c7

Browse files
authored
Merge pull request #1467 from ghalestrilo/feature/mobile-sketch-view
Mobile Sketch Preview Screen
2 parents 3ce4973 + b4c1b86 commit ff658c7

File tree

12 files changed

+386
-109
lines changed

12 files changed

+386
-109
lines changed

Diff for: client/common/icons.jsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import styled from 'styled-components';
4-
import { remSize, prop } from '../theme';
4+
import { prop } from '../theme';
55
import SortArrowUp from '../images/sort-arrow-up.svg';
66
import SortArrowDown from '../images/sort-arrow-down.svg';
77
import Github from '../images/github.svg';
@@ -10,6 +10,8 @@ import Plus from '../images/plus-icon.svg';
1010
import Close from '../images/close.svg';
1111
import Exit from '../images/exit.svg';
1212
import DropdownArrow from '../images/down-filled-triangle.svg';
13+
import Preferences from '../images/preferences.svg';
14+
import Play from '../images/triangle-arrow-right.svg';
1315

1416
// HOC that adds the right web accessibility props
1517
// https://www.scottohara.me/blog/2019/05/22/contextual-images-svgs-and-a11y.html
@@ -70,3 +72,5 @@ export const PlusIcon = withLabel(Plus);
7072
export const CloseIcon = withLabel(Close);
7173
export const ExitIcon = withLabel(Exit);
7274
export const DropdownArrowIcon = withLabel(DropdownArrow);
75+
export const PreferencesIcon = withLabel(Preferences);
76+
export const PlayIcon = withLabel(Play);

Diff for: client/components/mobile/Footer.jsx

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import { prop, remSize } from '../../theme';
4+
5+
const background = prop('MobilePanel.default.background');
6+
const textColor = prop('primaryTextColor');
7+
8+
const Footer = styled.div`
9+
position: fixed;
10+
width: 100%;
11+
background: ${background};
12+
color: ${textColor};
13+
padding: ${remSize(12)};
14+
padding-left: ${remSize(32)};
15+
z-index: 1;
16+
17+
bottom: 0;
18+
`;
19+
20+
export default Footer;

Diff for: client/components/mobile/Header.jsx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import { prop, remSize } from '../../theme';
4+
5+
const background = prop('MobilePanel.default.background');
6+
const textColor = prop('primaryTextColor');
7+
8+
const Header = styled.div`
9+
position: fixed;
10+
width: 100%;
11+
background: ${background};
12+
color: ${textColor};
13+
padding: ${remSize(12)};
14+
padding-left: ${remSize(16)};
15+
padding-right: ${remSize(16)};
16+
z-index: 1;
17+
18+
display: flex;
19+
flex: 1;
20+
flex-direction: row;
21+
justify-content: flex-start;
22+
align-items: center;
23+
24+
// TODO:
25+
svg {
26+
height: 2rem;
27+
}
28+
`;
29+
30+
export default Header;

Diff for: client/components/mobile/IDEWrapper.jsx

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import { remSize } from '../../theme';
4+
5+
export default styled.div`
6+
z-index: 0;
7+
margin-top: ${remSize(16)};
8+
`;

Diff for: client/components/mobile/IconButton.jsx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import styled from 'styled-components';
4+
import Button from '../../common/Button';
5+
6+
const ButtonWrapper = styled(Button)`
7+
width: 3rem;
8+
> svg {
9+
width: 100%;
10+
height: 100%;
11+
}
12+
`;
13+
14+
const IconButton = (props) => {
15+
const { icon, ...otherProps } = props;
16+
const Icon = icon;
17+
18+
return (<ButtonWrapper
19+
iconBefore={<Icon />}
20+
kind={Button.kinds.inline}
21+
focusable="false"
22+
{...otherProps}
23+
/>);
24+
};
25+
26+
IconButton.propTypes = {
27+
icon: PropTypes.func.isRequired
28+
};
29+
30+
export default IconButton;

Diff for: client/components/mobile/MobileScreen.jsx

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
const Screen = ({ children }) => (
5+
<div className="fullscreen-preview">
6+
{children}
7+
</div>
8+
);
9+
Screen.propTypes = {
10+
children: PropTypes.node.isRequired
11+
};
12+
13+
export default Screen;

Diff for: client/modules/IDE/components/PreviewFrame.jsx

+26-38
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,23 @@ import {
2222
import { hijackConsoleErrorsScript, startTag, getAllScriptOffsets }
2323
from '../../../utils/consoleUtils';
2424

25+
26+
const shouldRenderSketch = (props, prevProps = undefined) => {
27+
const { isPlaying, previewIsRefreshing, fullView } = props;
28+
29+
// if the user explicitly clicks on the play button
30+
if (isPlaying && previewIsRefreshing) return true;
31+
32+
if (!prevProps) return false;
33+
34+
return (props.isPlaying !== prevProps.isPlaying // if sketch starts or stops playing, want to rerender
35+
|| props.isAccessibleOutputPlaying !== prevProps.isAccessibleOutputPlaying // if user switches textoutput preferences
36+
|| props.textOutput !== prevProps.textOutput
37+
|| props.gridOutput !== prevProps.gridOutput
38+
|| props.soundOutput !== prevProps.soundOutput
39+
|| (fullView && props.files[0].id !== prevProps.files[0].id));
40+
};
41+
2542
class PreviewFrame extends React.Component {
2643
constructor(props) {
2744
super(props);
@@ -30,46 +47,17 @@ class PreviewFrame extends React.Component {
3047

3148
componentDidMount() {
3249
window.addEventListener('message', this.handleConsoleEvent);
50+
51+
const props = {
52+
...this.props,
53+
previewIsRefreshing: this.props.previewIsRefreshing,
54+
isAccessibleOutputPlaying: this.props.isAccessibleOutputPlaying
55+
};
56+
if (shouldRenderSketch(props)) this.renderSketch();
3357
}
3458

3559
componentDidUpdate(prevProps) {
36-
// if sketch starts or stops playing, want to rerender
37-
if (this.props.isPlaying !== prevProps.isPlaying) {
38-
this.renderSketch();
39-
return;
40-
}
41-
42-
// if the user explicitly clicks on the play button
43-
if (this.props.isPlaying && this.props.previewIsRefreshing) {
44-
this.renderSketch();
45-
return;
46-
}
47-
48-
// if user switches textoutput preferences
49-
if (this.props.isAccessibleOutputPlaying !== prevProps.isAccessibleOutputPlaying) {
50-
this.renderSketch();
51-
return;
52-
}
53-
54-
if (this.props.textOutput !== prevProps.textOutput) {
55-
this.renderSketch();
56-
return;
57-
}
58-
59-
if (this.props.gridOutput !== prevProps.gridOutput) {
60-
this.renderSketch();
61-
return;
62-
}
63-
64-
if (this.props.soundOutput !== prevProps.soundOutput) {
65-
this.renderSketch();
66-
return;
67-
}
68-
69-
if (this.props.fullView && this.props.files[0].id !== prevProps.files[0].id) {
70-
this.renderSketch();
71-
}
72-
60+
if (shouldRenderSketch(this.props, prevProps)) this.renderSketch();
7361
// small bug - if autorefresh is on, and the usr changes files
7462
// in the sketch, preview will reload
7563
}
@@ -398,7 +386,7 @@ PreviewFrame.propTypes = {
398386
clearConsole: PropTypes.func.isRequired,
399387
cmController: PropTypes.shape({
400388
getContent: PropTypes.func
401-
})
389+
}),
402390
};
403391

404392
PreviewFrame.defaultProps = {

Diff for: client/modules/IDE/pages/IDEViewMobile.jsx

+32-69
Original file line numberDiff line numberDiff line change
@@ -20,93 +20,54 @@ import { getHTMLFile } from '../reducers/files';
2020

2121
// Local Imports
2222
import Editor from '../components/Editor';
23-
import { prop, remSize } from '../../../theme';
24-
import { ExitIcon } from '../../../common/icons';
25-
26-
const background = prop('Button.default.background');
27-
const textColor = prop('primaryTextColor');
28-
29-
30-
const Header = styled.div`
31-
position: fixed;
32-
width: 100%;
33-
background: ${background};
34-
color: ${textColor};
35-
padding: ${remSize(12)};
36-
padding-left: ${remSize(32)};
37-
padding-right: ${remSize(32)};
38-
z-index: 1;
39-
40-
display: flex;
41-
flex: 1;
42-
flex-direction: row;
43-
justify-content: flex-start;
44-
align-items: center;
45-
`;
46-
47-
const Footer = styled.div`
48-
position: fixed;
49-
width: 100%;
50-
background: ${background};
51-
color: ${textColor};
52-
padding: ${remSize(12)};
53-
padding-left: ${remSize(32)};
54-
z-index: 1;
55-
56-
bottom: 0;
57-
`;
58-
59-
const Content = styled.div`
60-
z-index: 0;
61-
margin-top: ${remSize(16)};
62-
`;
23+
import { PreferencesIcon, PlayIcon, ExitIcon } from '../../../common/icons';
6324

64-
const Icon = styled.a`
65-
> svg {
66-
fill: ${textColor};
67-
color: ${textColor};
68-
margin-left: ${remSize(16)};
69-
}
70-
`;
25+
import IconButton from '../../../components/mobile/IconButton';
26+
import Header from '../../../components/mobile/Header';
27+
import Screen from '../../../components/mobile/MobileScreen';
28+
import Footer from '../../../components/mobile/Footer';
29+
import IDEWrapper from '../../../components/mobile/IDEWrapper';
30+
import { remSize } from '../../../theme';
7131

72-
const IconLinkWrapper = styled(Link)`
73-
width: 3rem;
74-
margin-right: 1.25rem;
75-
margin-left: none;
32+
const IconContainer = styled.div`
33+
margin-left: ${remSize(32)};
34+
display: flex;
7635
`;
7736

78-
79-
const Screen = ({ children }) => (
80-
<div className="fullscreen-preview">
81-
{children}
82-
</div>
83-
);
84-
Screen.propTypes = {
85-
children: PropTypes.node.isRequired
86-
};
87-
8837
const isUserOwner = ({ project, user }) => (project.owner && project.owner.id === user.id);
8938

9039
const IDEViewMobile = (props) => {
9140
const {
92-
preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage, selectedFile, updateFileContent, files, closeEditorOptions, showEditorOptions, showKeyboardShortcutModal, setUnsavedChanges, startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, showRuntimeErrorWarning, hideRuntimeErrorWarning
41+
preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage,
42+
selectedFile, updateFileContent, files,
43+
closeEditorOptions, showEditorOptions, showKeyboardShortcutModal, setUnsavedChanges,
44+
startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console,
45+
showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch
9346
} = props;
9447

95-
const [tmController, setTmController] = useState(null);
48+
const [tmController, setTmController] = useState(null); // eslint-disable-line
49+
const [overlay, setOverlay] = useState(null); // eslint-disable-line
9650

9751
return (
9852
<Screen>
9953
<Header>
100-
<IconLinkWrapper to="/" aria-label="Return to original editor">
101-
<ExitIcon />
102-
</IconLinkWrapper>
103-
<div>
54+
<IconButton to="/mobile" icon={ExitIcon} aria-label="Return to original editor" />
55+
<div style={{ marginLeft: '1rem' }}>
10456
<h2>{project.name}</h2>
10557
<h3>{selectedFile.name}</h3>
10658
</div>
59+
60+
<IconContainer>
61+
<IconButton
62+
onClick={() => setOverlay('preferences')}
63+
icon={PreferencesIcon}
64+
aria-label="Open preferences menu"
65+
/>
66+
<IconButton to="/mobile/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" />
67+
</IconContainer>
10768
</Header>
10869

109-
<Content>
70+
<IDEWrapper>
11071
<Editor
11172
lintWarning={preferences.lintWarning}
11273
linewrap={preferences.linewrap}
@@ -141,7 +102,7 @@ const IDEViewMobile = (props) => {
141102
runtimeErrorWarningVisible={ide.runtimeErrorWarningVisible}
142103
provideController={setTmController}
143104
/>
144-
</Content>
105+
</IDEWrapper>
145106
<Footer><h2>Bottom Bar</h2></Footer>
146107
</Screen>
147108
);
@@ -205,6 +166,8 @@ IDEViewMobile.propTypes = {
205166
updatedAt: PropTypes.string
206167
}).isRequired,
207168

169+
startSketch: PropTypes.func.isRequired,
170+
208171
updateLintMessage: PropTypes.func.isRequired,
209172

210173
clearLintMessage: PropTypes.func.isRequired,

0 commit comments

Comments
 (0)