Skip to content

Commit f703f1b

Browse files
authored
Crash on missing platform support (#1713)
The Simplenote app, for better or worse, currently depends on a few storage technologies that may be disabled through browser settings, extensions, and manual configuration. When these dependencies are missing we cannot function normally and right now the app crashes or breaks in misleading ways. In this patch we perform an up-front check when the app boots to verify that we have some basic dependencies met. This is not an exhaustive validation but it should catch the most common cases and give customers an idea of why things may be broken.
1 parent 00564a8 commit f703f1b

File tree

4 files changed

+95
-10
lines changed

4 files changed

+95
-10
lines changed

lib/boot.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import './utils/ensure-platform-support';
12
import './utils/ensure-single-browser-tab-only';
23
import 'core-js/stable';
34
import 'regenerator-runtime/runtime';
Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
32

43
import './style';
54

6-
const BootWarning = () => {
7-
return (
8-
<h3 className="boot-warning__message">
9-
Simplenote cannot be opened simultaneously in more than one tab or window
10-
per browser.
11-
</h3>
12-
);
13-
};
5+
const BootWarning = ({ children }) => (
6+
<h3 className="boot-warning__message">{children}</h3>
7+
);
148

159
export default BootWarning;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import { parse as parseCookie } from 'cookie';
4+
5+
import BootWarning from '../components/boot-warning';
6+
7+
const hasLocalStorage = () => {
8+
try {
9+
localStorage.setItem('__localStorageSentinel__', 'present');
10+
localStorage.removeItem('__localStorageSentinel__');
11+
return true;
12+
} catch (e) {
13+
return false;
14+
}
15+
};
16+
17+
const hasIndexedDB = () => {
18+
try {
19+
const opener = indexedDB.open('simplenote_sentinel');
20+
if (!(opener instanceof IDBOpenDBRequest)) {
21+
return false;
22+
}
23+
const closer = () => indexedDB.deleteDatabase('simplenote_sentinel');
24+
opener.onerror = closer;
25+
opener.onsuccess = closer;
26+
return true;
27+
} catch (e) {
28+
return false;
29+
}
30+
};
31+
32+
const hasCookies = () => {
33+
try {
34+
if (!navigator.cookieEnabled) {
35+
return false;
36+
}
37+
38+
const cookie = document.cookie;
39+
40+
const now = Date.now().toString();
41+
document.cookie = `__now=${now};`;
42+
const parsed = parseCookie(document.cookie);
43+
const didSet = parsed.__now === now;
44+
document.cookie = cookie;
45+
46+
return didSet;
47+
} catch (e) {
48+
return false;
49+
}
50+
};
51+
52+
const deps = [
53+
['localStorage', hasLocalStorage()],
54+
['indexedDB', hasIndexedDB()],
55+
['cookies', hasCookies()],
56+
];
57+
58+
const missingDeps = deps.filter(([, hasIt]) => !hasIt).map(([name]) => name);
59+
60+
if (missingDeps.length) {
61+
ReactDOM.render(
62+
<BootWarning>
63+
<p>
64+
Simplenote depends on a few web technologies to operate. Please make
65+
sure that you have all of the following enabled in your browser.
66+
</p>
67+
<ul>
68+
{deps.map(([name, hasIt]) => (
69+
<li key={name}>
70+
{hasIt ? '✅' : '⚠️'} {name} - {hasIt ? 'working' : 'missing'}
71+
</li>
72+
))}
73+
</ul>
74+
<p>
75+
Many browsers disable some of these features in Private Mode. Simplenote
76+
does not currently support running in Private Mode.
77+
</p>
78+
</BootWarning>,
79+
document.getElementById('root')
80+
);
81+
throw new Error(
82+
`Simplenote is missing required dependencies: ${missingDeps.join(', ')}`
83+
);
84+
}

lib/utils/ensure-single-browser-tab-only.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ const foundElectron = window.process && window.process.type;
1010

1111
if (!foundElectron) {
1212
if ('lock-acquired' !== grabSessionLock()) {
13-
ReactDOM.render(<BootWarning />, document.getElementById('root'));
13+
ReactDOM.render(
14+
<BootWarning>
15+
Simplenote cannot be opened simultaneously in more than one tab or
16+
window per browser.
17+
</BootWarning>,
18+
document.getElementById('root')
19+
);
1420
throw new Error('Simplenote can only be opened in one tab');
1521
}
1622
let keepGoing = true;

0 commit comments

Comments
 (0)