Skip to content

Commit b04d73c

Browse files
committed
Show update notification
1 parent f8df5c1 commit b04d73c

File tree

10 files changed

+133
-17
lines changed

10 files changed

+133
-17
lines changed

packages/app/src/app/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,17 @@ requirePolyfills().then(() => {
7878
message,
7979
});
8080

81-
registerServiceWorker('/service-worker.js', showNotification);
81+
registerServiceWorker('/service-worker.js', {
82+
onUpdate: () => {
83+
controller.getSignal('setUpdateStatus')({ status: 'available' });
84+
},
85+
onInstalled: () => {
86+
showNotification(
87+
'CodeSandbox has been installed, it now works offline.',
88+
'success'
89+
);
90+
},
91+
});
8292

8393
try {
8494
render(
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import styled from 'styled-components';
2+
3+
export const UpdateMessage = styled.div`
4+
color: white;
5+
border-radius: 2px;
6+
font-size: 0.75rem;
7+
width: 100px;
8+
text-align: center;
9+
padding: 0.5rem 0.75rem;
10+
11+
cursor: pointer;
12+
13+
&::after {
14+
content: 'Update Available!';
15+
}
16+
17+
&:hover {
18+
&::after {
19+
content: 'Click to Refresh';
20+
}
21+
}
22+
`;
23+
24+
export const UpdateContainer = styled.div`
25+
cursor: pointer;
26+
27+
&:hover {
28+
> #update-message {
29+
&::after {
30+
content: 'Click to Refresh' !important;
31+
}
32+
}
33+
}
34+
`;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import Tooltip from 'common/components/Tooltip';
3+
import RefreshIcon from 'react-icons/lib/md/refresh';
4+
5+
import { UpdateContainer, UpdateMessage } from './elements';
6+
7+
export default class UpdateFound extends React.PureComponent {
8+
state = {
9+
showTooltipManually: true,
10+
};
11+
12+
componentDidMount() {
13+
this.timeout = setTimeout(() => {
14+
this.setState({ showTooltipManually: false });
15+
}, 10000);
16+
}
17+
18+
componentWillUnmount() {
19+
if (this.timeout) {
20+
clearTimeout(this.timeout);
21+
}
22+
}
23+
24+
render() {
25+
return (
26+
<UpdateContainer {...this.props}>
27+
<Tooltip
28+
theme="update"
29+
html={
30+
<UpdateMessage
31+
id="update-message"
32+
onClick={() => document.location.reload()}
33+
/>
34+
}
35+
open={this.state.showTooltipManually}
36+
trigger={
37+
this.state.showTooltipManually ? 'manual' : 'mouseenter focus'
38+
}
39+
arrow
40+
distance={15}
41+
>
42+
<RefreshIcon />
43+
</Tooltip>
44+
</UpdateContainer>
45+
);
46+
}
47+
}

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ import PatronBadge from '-!svg-react-loader!common/utils/badges/svg/patron-4.svg
1717
import Margin from 'common/components/spacing/Margin';
1818
import HeaderSearchBar from 'app/components/HeaderSearchBar';
1919
import UserMenu from 'app/pages/common/UserMenu';
20+
import theme from 'common/theme';
2021

2122
import Logo from './Logo';
2223
import Action from './Action';
2324

2425
import { Container, Right, Left } from './elements';
2526

26-
function Header({ store, signals }) {
27+
import UpdateFound from './UpdateFound';
28+
29+
const Header = ({ store, signals }) => {
2730
const sandbox = store.editor.currentSandbox;
2831

2932
return (
@@ -93,6 +96,18 @@ function Header({ store, signals }) {
9396
<HeaderSearchBar />
9497
</div>
9598

99+
{store.updateStatus === 'available' && (
100+
<Action
101+
onClick={() => document.location.reload()}
102+
Icon={UpdateFound}
103+
style={{
104+
color: theme.green(),
105+
fontSize: '1rem',
106+
}}
107+
tooltip="Update Available! Click to Refresh."
108+
/>
109+
)}
110+
96111
{!store.isLoggedIn ||
97112
(!store.isPatron && (
98113
<Action
@@ -151,6 +166,6 @@ function Header({ store, signals }) {
151166
</Right>
152167
</Container>
153168
);
154-
}
169+
};
155170

156171
export default inject('signals', 'store')(observer(Header));

packages/app/src/app/store/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export default Module({
5454
uploadedFiles: null,
5555
maxStorage: 0,
5656
usedStorage: 0,
57+
updateStatus: null,
5758
},
5859
getters: {
5960
isPatron,
@@ -84,6 +85,7 @@ export default Module({
8485
signInGithubClicked: sequences.signInGithub,
8586
signOutClicked: sequences.signOut,
8687
signOutGithubIntegration: sequences.signOutGithubIntegration,
88+
setUpdateStatus: sequences.setUpdateStatus,
8789
},
8890
catch: [[errors.AuthenticationError, sequences.showAuthenticationError]],
8991
modules: {

packages/app/src/app/store/model.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,5 @@ export default {
7575
),
7676
maxStorage: types.number,
7777
usedStorage: types.number,
78+
updateStatus: types.maybe(types.string),
7879
};

packages/app/src/app/store/sequences.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,5 @@ export const loadSandbox = factories.withLoadApp([
319319
},
320320
set(state`editor.isLoading`, false),
321321
]);
322+
323+
export const setUpdateStatus = [set(state`updateStatus`, props`status`)];

packages/app/src/sandbox/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function getId() {
3131
}
3232

3333
requirePolyfills().then(() => {
34-
registerServiceWorker('/sandbox-service-worker.js');
34+
registerServiceWorker('/sandbox-service-worker.js', {});
3535

3636
function sendReady() {
3737
dispatch({ type: 'initialized' });

packages/common/components/Tooltip.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { injectGlobal } from 'styled-components';
22
import 'react-tippy/dist/tippy.css';
33
import { Tooltip, withTooltip } from 'react-tippy';
4+
import theme from 'common/theme';
45

56
// eslint-disable-next-line
67
injectGlobal`
@@ -10,6 +11,16 @@ injectGlobal`
1011
.tippy-tooltip [x-circle] {
1112
background-color: rgb(21, 24, 25) !important;
1213
}
14+
15+
.tippy-tooltip.update-theme {
16+
.arrow-regular {
17+
border-bottom: 7px solid ${theme.green()} !important;
18+
}
19+
20+
background-color: ${theme.green()};
21+
border-radius: 2px;
22+
padding: 0 !important;
23+
}
1324
`;
1425

1526
export default Tooltip;

packages/common/registerServiceWorker.js

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const isLocalhost = Boolean(
2020

2121
const isHttp = Boolean(window.location.protocol === 'http:');
2222

23-
export default function register(swUrl, sendNotification) {
23+
export default function register(swUrl, { onUpdate, onInstalled }) {
2424
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
2525
// The URL constructor is available in all browsers that support SW.
2626
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
@@ -34,7 +34,7 @@ export default function register(swUrl, sendNotification) {
3434
window.addEventListener('load', () => {
3535
if (!isLocalhost && !isHttp) {
3636
// It's neither localhost nor http. Just register service worker
37-
registerValidSW(swUrl, sendNotification);
37+
registerValidSW(swUrl, { onUpdate, onInstalled });
3838
} else if (isLocalhost) {
3939
// This is running on localhost. Lets check if a service worker still exists or not.
4040
checkValidServiceWorker(swUrl);
@@ -43,7 +43,7 @@ export default function register(swUrl, sendNotification) {
4343
}
4444
}
4545

46-
function registerValidSW(swUrl, sendNotification) {
46+
function registerValidSW(swUrl, { onUpdate, onInstalled }) {
4747
navigator.serviceWorker
4848
.register(swUrl)
4949
.then(registration => {
@@ -56,21 +56,15 @@ function registerValidSW(swUrl, sendNotification) {
5656
// the fresh content will have been added to the cache.
5757
// It's the perfect time to display a "New content is
5858
// available; please refresh." message in your web app.
59-
if (sendNotification) {
60-
sendNotification(
61-
'CodeSandbox received an update, refresh to see it!',
62-
'notice'
63-
);
59+
if (onUpdate) {
60+
onUpdate();
6461
}
6562
} else {
6663
// At this point, everything has been precached.
6764
// It's the perfect time to display a
6865
// "Content is cached for offline use." message.
69-
if (sendNotification) {
70-
sendNotification(
71-
'CodeSandbox has been cached, it now works offline.',
72-
'success'
73-
);
66+
if (onInstalled) {
67+
onInstalled();
7468
}
7569
}
7670
} else if (installingWorker.state === 'redundant') {

0 commit comments

Comments
 (0)