Skip to content

Commit 9f094be

Browse files
authored
Submit Feedback (codesandbox#1386)
1 parent d56d472 commit 9f094be

File tree

14 files changed

+380
-55
lines changed

14 files changed

+380
-55
lines changed

packages/app/babel.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
'babel-plugin-lodash',
1717
'babel-plugin-system-import-transformer',
1818
'babel-plugin-macros',
19+
'babel-plugin-styled-components',
1920
],
2021
},
2122
},

packages/app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"babel-loader": "8.0.2",
4545
"babel-plugin-codegen": "^1.2.0",
4646
"babel-plugin-lodash": "^3.3.4",
47-
"babel-plugin-styled-components": "^1.9.2",
47+
"babel-plugin-styled-components": "^1.9.4",
4848
"babel-plugin-syntax-dynamic-import": "^6.18.0",
4949
"babel-plugin-transform-class-properties": "^6.24.1",
5050
"babel-plugin-transform-object-rest-spread": "^6.23.0",
@@ -118,6 +118,7 @@
118118
"@emmetio/codemirror-plugin": "^0.3.5",
119119
"@svgr/core": "^2.4.1",
120120
"@vue/babel-preset-app": "^3.2.0",
121+
"airtable": "^0.5.8",
121122
"apollo-boost": "^0.1.7",
122123
"apollo-link-batch-http": "^1.2.2",
123124
"apollo-link-context": "^1.0.8",

packages/app/src/app/pages/Sandbox/Editor/Header/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import CollectionInfo from './CollectionInfo';
3030
import { Container, Right, Left, Centered } from './elements';
3131

3232
import UpdateFound from './UpdateFound';
33+
import FeedbackIcon from '../../../common/UserMenu/Menu/FeedbackIcon';
3334

3435
const Header = ({ store, signals }) => {
3536
const sandbox = store.editor.currentSandbox;
@@ -196,6 +197,18 @@ const Header = ({ store, signals }) => {
196197
/>
197198
)}
198199

200+
{!store.isLoggedIn && (
201+
<Action
202+
onClick={() =>
203+
signals.modalOpened({
204+
modal: 'feedback',
205+
})
206+
}
207+
tooltip="Submit Feedback"
208+
Icon={FeedbackIcon}
209+
/>
210+
)}
211+
199212
<Margin
200213
style={{
201214
zIndex: 20,
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import * as React from 'react';
2+
import { inject, observer } from 'mobx-react';
3+
import Margin from 'common/components/spacing/Margin';
4+
import Button from 'app/components/Button';
5+
6+
import AutosizeTextArea from 'common/components/AutosizeTextArea';
7+
import pushToAirtable from 'app/store/utils/pushToAirtable';
8+
9+
import { EmojiButton } from './elements';
10+
11+
function sendFeedback(props) {
12+
const { feedback, emoji, sandboxId, user } = props;
13+
const { username, email } = user || {};
14+
15+
return pushToAirtable({ feedback, emoji, sandboxId, username, email });
16+
}
17+
18+
class Feedback extends React.Component {
19+
state = {
20+
feedback: '',
21+
emoji: null,
22+
loading: false,
23+
};
24+
25+
onChange = e => {
26+
this.setState({ feedback: e.target.value });
27+
};
28+
29+
onSubmit = evt => {
30+
const { id, user, signals } = this.props;
31+
const { feedback, emoji } = this.state;
32+
evt.preventDefault();
33+
34+
this.setState({ loading: true }, () => {
35+
sendFeedback({
36+
sandboxId: id,
37+
feedback,
38+
emoji,
39+
user,
40+
})
41+
.then(() => {
42+
this.setState(
43+
{
44+
feedback: '',
45+
emoji: null,
46+
loading: false,
47+
},
48+
() => {
49+
signals.modalClosed();
50+
51+
signals.notificationAdded({
52+
message: `Thanks for your feedback!`,
53+
type: 'success',
54+
});
55+
}
56+
);
57+
})
58+
.catch(e => {
59+
signals.notificationAdded({
60+
message: `Something went wrong while sending feedback: ${
61+
e.message
62+
}`,
63+
type: 'error',
64+
});
65+
66+
this.setState({ loading: false });
67+
});
68+
});
69+
};
70+
71+
setHappy = () => {
72+
this.setState({ emoji: 'happy' });
73+
};
74+
setSad = () => {
75+
this.setState({ emoji: 'sad' });
76+
};
77+
78+
render() {
79+
const { feedback, emoji } = this.state;
80+
return (
81+
<form onSubmit={this.onSubmit}>
82+
<AutosizeTextArea
83+
css={`
84+
width: 100%;
85+
`}
86+
value={feedback}
87+
onChange={this.onChange}
88+
placeholder="What are your thoughts?"
89+
minRows={3}
90+
/>
91+
92+
<Margin
93+
top={0.5}
94+
css={`
95+
display: flex;
96+
align-items: center;
97+
`}
98+
>
99+
<EmojiButton
100+
type="button"
101+
active={emoji === 'happy'}
102+
onClick={this.setHappy}
103+
>
104+
<span role="img" aria-label="happy">
105+
😊
106+
</span>
107+
</EmojiButton>
108+
109+
<EmojiButton
110+
type="button"
111+
active={emoji === 'sad'}
112+
onClick={this.setSad}
113+
>
114+
<span role="img" aria-label="sad">
115+
😞
116+
</span>
117+
</EmojiButton>
118+
119+
<div
120+
css={`
121+
flex: 1;
122+
`}
123+
>
124+
<Button
125+
disabled={this.state.loading}
126+
small
127+
css={`
128+
float: right;
129+
`}
130+
>
131+
{this.state.loading ? 'Sending...' : 'Submit'}
132+
</Button>
133+
</div>
134+
</Margin>
135+
</form>
136+
);
137+
}
138+
}
139+
140+
export default inject('signals')(observer(Feedback));
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import styled, { css } from 'styled-components';
2+
3+
export const EmojiButton = styled.button`
4+
transition: 0.3s ease all;
5+
border: 2px solid rgba(0, 0, 0, 0.2);
6+
background-color: rgba(0, 0, 0, 0.3);
7+
border-radius: 4px;
8+
padding: 0.1rem;
9+
outline: 0;
10+
margin-right: 1rem;
11+
width: 32px;
12+
height: 32px;
13+
line-height: 28px;
14+
display: inline-flex;
15+
align-items: center;
16+
justify-content: center;
17+
18+
cursor: pointer;
19+
20+
span {
21+
display: inline;
22+
line-height: initial;
23+
width: 15px;
24+
}
25+
26+
&:hover {
27+
border: 2px solid rgba(255, 255, 255, 0.2);
28+
background-color: ${props => props.theme.secondary};
29+
}
30+
&:focus {
31+
border: 2px solid rgba(255, 255, 255, 0.2);
32+
}
33+
34+
${props =>
35+
props.active &&
36+
css`
37+
border: 2px solid rgba(255, 255, 255, 0.2);
38+
background-color: ${props.theme.secondary};
39+
`};
40+
`;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
3+
import { inject } from 'mobx-react';
4+
import Loadable from 'app/utils/Loadable';
5+
6+
import { Heading, Container } from '../elements';
7+
8+
const Feedback = Loadable(() =>
9+
import(/* webpackChunkName: 'feedback' */ './Feedback')
10+
);
11+
12+
const FeedbackModal = ({ store }) => (
13+
<Container>
14+
<Heading>Submit Feedback</Heading>
15+
16+
<Feedback user={store.user} id={store.editor.currentId} />
17+
</Container>
18+
);
19+
20+
export default inject('store')(FeedbackModal);

packages/app/src/app/pages/common/Modals/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import StorageManagementModal from './StorageManagementModal';
2323
import ForkServerModal from './ForkServerModal';
2424
import PrivacyServerWarning from './PrivacyServerWarning';
2525
import PickSandboxModal from './PickSandboxModal';
26+
import FeedbackModal from './FeedbackModal';
2627

2728
const MoveSandboxFolderModal = Loadable(() =>
2829
import('./MoveSandboxFolderModal')
@@ -109,6 +110,10 @@ const modals = {
109110
Component: MoveSandboxFolderModal,
110111
width: 350,
111112
},
113+
feedback: {
114+
Component: FeedbackModal,
115+
width: 450,
116+
},
112117
};
113118

114119
function Modals({ store, signals }) {

packages/app/src/app/pages/common/Navigation/index.js

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@ import * as React from 'react';
22
import { inject, observer } from 'mobx-react';
33
import { Link } from 'react-router-dom';
44
import Media from 'react-media';
5-
import { patronUrl, dashboardUrl, searchUrl } from 'common/utils/url-generator';
5+
import { patronUrl, searchUrl } from 'common/utils/url-generator';
66

77
import SearchIcon from 'react-icons/lib/go/search';
88
import PlusIcon from 'react-icons/lib/go/plus';
99
import BellIcon from 'react-icons/lib/md/notifications';
10-
import BookIcon from 'react-icons/lib/md/library-books';
1110
import Row from 'common/components/flex/Row';
1211
import Tooltip from 'common/components/Tooltip';
1312
import PatronBadge from '-!svg-react-loader!common/utils/badges/svg/patron-4.svg'; // eslint-disable-line import/no-webpack-loader-syntax
1413
import HeaderSearchBar from 'app/components/HeaderSearchBar';
15-
import InfoIcon from 'app/pages/Sandbox/Editor/Navigation/InfoIcon';
1614
import OverlayComponent from 'app/components/Overlay';
1715
import Notifications from './Notifications';
1816

@@ -58,13 +56,6 @@ function Navigation({ signals, store, title, searchNoInput }) {
5856
}
5957
</Media>
6058
</Action>
61-
<Action>
62-
<Tooltip position="bottom" title="Documentation">
63-
<a style={{ color: 'white' }} href="/docs">
64-
<BookIcon height={35} />
65-
</a>
66-
</Tooltip>
67-
</Action>
6859
{!isPatron && (
6960
<Action>
7061
<Tooltip position="bottom" title="Support CodeSandbox">
@@ -88,16 +79,6 @@ function Navigation({ signals, store, title, searchNoInput }) {
8879
</Tooltip>
8980
</Action>
9081

91-
{user && (
92-
<Action style={{ fontSize: '1.125rem' }}>
93-
<Tooltip position="bottom" title="Dashboard">
94-
<Link style={{ color: 'white' }} to={dashboardUrl()}>
95-
<InfoIcon height={35} />
96-
</Link>
97-
</Tooltip>
98-
</Action>
99-
)}
100-
10182
{user && (
10283
<OverlayComponent
10384
isOpen={store.userNotifications.notificationsOpened}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
3+
export default () => (
4+
<svg
5+
xmlnsXlink="http://www.w3.org/1999/xlink"
6+
version="1.1"
7+
x="0px"
8+
y="0px"
9+
viewBox="0 0 24 24"
10+
enableBackground="new 0 0 24 24"
11+
xmlSpace="preserve"
12+
width="1em"
13+
height="1em"
14+
style={{ fill: 'currentColor' }}
15+
>
16+
<path d="M21.735 7.609c.169.509.258 1.037.258 1.58 0 3.691-4.108 6.715-9.33 7.004a14.12 14.12 0 0 1-3.02 2.212c1.034.324 2.162.531 3.347.596 1.574 1.518 4.069 3.183 7.837 3.936 0 0-2.3-1.381-1.957-4.849 3.041-1.213 5.089-3.487 5.089-6.091 0-1.659-.833-3.185-2.224-4.388z" />
17+
<path d="M10.19 1.061C4.584 1.061.041 4.206.041 8.088c0 .145.006.291.019.434.214 2.422 2.199 4.512 5.07 5.658.344 3.467-1.957 4.847-1.957 4.847 3.768-.753 6.263-2.417 7.837-3.937 5.222-.288 9.331-3.314 9.331-7.003-.001-3.88-4.546-7.026-10.151-7.026zm4.4 9.659H5.221c-.431 0-.782-.332-.782-.742 0-.411.35-.742.782-.742h9.369c.431 0 .781.332.781.742-.001.411-.35.742-.781.742zm0-3.548H5.221c-.431 0-.782-.332-.782-.741s.35-.741.782-.741h9.369c.431 0 .781.332.781.741-.001.409-.35.741-.781.741z" />
18+
</svg>
19+
);

packages/app/src/app/pages/common/UserMenu/Menu/elements.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import delayEffect from 'common/utils/animation/delay-effect';
33

44
export const Container = styled.div`
55
position: absolute;
6-
background-color: ${props => props.theme.background2.darken(0.5)};
6+
background-color: ${props => props.theme.background4};
77
box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.75);
88
99
${delayEffect(0)};
@@ -39,4 +39,16 @@ export const Item = styled.div`
3939

4040
export const Icon = styled.span`
4141
margin-right: 0.75rem;
42+
display: inline-flex;
43+
align-items: center;
44+
`;
45+
46+
export const Separator = styled.hr`
47+
height: 1px;
48+
width: 100%;
49+
margin: 0.5rem 0;
50+
51+
background-color: ${props => props.theme.background};
52+
border: 0;
53+
outline: 0;
4254
`;

0 commit comments

Comments
 (0)