diff --git a/scripts/build-release.js b/scripts/build-release.js
index 426ab9db3..df2047e4f 100755
--- a/scripts/build-release.js
+++ b/scripts/build-release.js
@@ -17,6 +17,8 @@ const {
getVersionNumber,
genMercurialRevision,
} = require('./build-utils.js');
+const fetch = require('node-fetch');
+const {ICONS, getIconURL} = require('../src/utils/icons.js');
function generateManifest(versionNumber) {
const filePath = path.join(__dirname, '..', 'dist');
@@ -112,11 +114,51 @@ function copyStaticFolder(buildFolder) {
});
}
+function downloadIcons(buildFolder) {
+ const iconURLs = Object.entries(ICONS).reduce((acc, [name, sizes]) => {
+ acc.push(
+ // get icons in @1x and @2x
+ ...sizes.map(size => ({name, size, density: 1})),
+ ...sizes.map(size => ({name, size, density: 2})),
+ );
+ return acc;
+ }, []);
+
+ return Promise.all(
+ iconURLs.map(({name, size, density}) =>
+ fetch(getIconURL(name, size, density))
+ .then(res => {
+ if (res.status !== 200) {
+ throw new Error(`Could not download the icon: ${name}`);
+ }
+ return res;
+ })
+ .then(
+ res =>
+ new Promise((resolve, reject) => {
+ const fileStream = fs.createWriteStream(
+ path.join(
+ buildFolder,
+ 'icons',
+ `${name}-${size}@${density}x.png`,
+ ),
+ );
+ res.body.pipe(fileStream);
+ res.body.on('error', reject);
+ fileStream.on('finish', resolve);
+ }),
+ )
+ .catch(console.error),
+ ),
+ );
+}
+
(async () => {
const dir = await buildFolder();
// eslint-disable-next-line no-console
console.log('Created build directory', dir);
copyStaticFolder(dir);
+ await downloadIcons(dir);
await compileDefaultPlugins(path.join(dir, 'defaultPlugins'));
await compile(dir, path.join(__dirname, '..', 'src', 'init.js'));
const versionNumber = getVersionNumber();
diff --git a/src/init.js b/src/init.js
index 1e0b59909..75ccbd140 100644
--- a/src/init.js
+++ b/src/init.js
@@ -8,7 +8,6 @@
import {Provider} from 'react-redux';
import ReactDOM from 'react-dom';
import {ContextMenuProvider} from 'flipper';
-import {precachedIcons} from './utils/icons.js';
import GK from './fb-stubs/GK.js';
import {init as initLogger} from './fb-stubs/Logger';
import App from './App.js';
@@ -49,23 +48,6 @@ const AppFrame = () => (
function init() {
// $FlowFixMe: this element exists!
ReactDOM.render(, document.getElementById('root'));
- // $FlowFixMe: service workers exist!
- navigator.serviceWorker
- .register(
- process.env.NODE_ENV === 'production'
- ? path.join(__dirname, 'serviceWorker.js')
- : './serviceWorker.js',
- )
- .then((r: ServiceWorkerRegistration) => {
- const client = r.installing || r.active;
- if (client != null) {
- client.postMessage({precachedIcons});
- } else {
- console.error('Service worker registration failed: ', r);
- }
- })
- .catch(console.error);
-
initLauncherHooks(config(), store);
const sessionId = store.getState().application.sessionId;
initCrashReporter(sessionId || '');
diff --git a/src/ui/components/Glyph.js b/src/ui/components/Glyph.js
index f64203d3c..461152536 100644
--- a/src/ui/components/Glyph.js
+++ b/src/ui/components/Glyph.js
@@ -7,8 +7,8 @@
import React from 'react';
import styled from '../styled/index.js';
-const PropTypes = require('prop-types');
-import {getIconUrl} from '../../utils/icons.js';
+import PropTypes from 'prop-types';
+import {getIconURL} from '../../utils/icons.js';
const ColoredIconBlack = styled('img')(({size}) => ({
height: size,
@@ -91,7 +91,11 @@ export default class Glyph extends React.Component<{
className={className}
color={color}
size={size}
- src={getIconUrl(name, size, variant)}
+ src={getIconURL(
+ variant === 'outline' ? `${name}-outline` : name,
+ size,
+ typeof window !== 'undefined' ? window.devicePixelRatio : 1,
+ )}
/>
);
}
diff --git a/src/utils/icons.js b/src/utils/icons.js
index 7e82897dd..34f98763f 100644
--- a/src/utils/icons.js
+++ b/src/utils/icons.js
@@ -5,133 +5,88 @@
* @format
*/
-// list of icons that are prefetched in the service worker when launching the app
-export const precachedIcons: Array = [
- {
- name: 'arrow-right',
- size: 12,
- },
- {
- name: 'caution-octagon',
- },
- {
- name: 'caution-triangle',
- },
- {
- name: 'info-circle',
- },
- {
- name: 'magic-wand',
- size: 20,
- },
- {
- name: 'magnifying-glass',
- },
- {
- name: 'minus-circle',
- size: 12,
- },
- {
- name: 'mobile',
- size: 12,
- },
- {
- name: 'box',
- size: 12,
- },
- {
- name: 'desktop',
- size: 12,
- },
- {
- name: 'bug',
- size: 12,
- },
- {
- name: 'posts',
- size: 20,
- },
- {
- name: 'rocket',
- size: 20,
- },
- {
- name: 'tools',
- size: 20,
- },
- {
- name: 'triangle-down',
- size: 12,
- },
- {
- name: 'triangle-right',
- size: 12,
- },
- {
- name: 'chevron-right',
- size: 8,
- },
- {
- name: 'chevron-down',
- size: 8,
- },
- {
- name: 'star',
- size: 16,
- variant: 'filled',
- },
- {
- name: 'star',
- size: 16,
- variant: 'outline',
- },
-].map(icon =>
- getIconUrl(icon.name, icon.size || undefined, icon.variant || undefined),
-);
+/* This file needs to be plain JS to be imported by scripts/build-release.js */
+/* eslint-disable import/no-commonjs */
-export function getIconUrl(
- name: string,
- size?: number = 16,
- variant?: 'filled' | 'outline' = 'filled',
-): string {
- if (name.indexOf('/') > -1) {
- return name;
- }
+const AVAILABLE_SIZES = [8, 10, 12, 16, 18, 20, 24, 32];
+const DENSITIES = [1, 1.5, 2, 3, 4];
+const fs = require('fs');
+const path = require('path');
+const {remote} = require('electron');
- const AVAILABLE_SIZES = [8, 10, 12, 16, 18, 20, 24, 32];
- const SCALE = [1, 1.5, 2, 3, 4];
+module.exports = {
+ ICONS: {
+ 'arrow-right': [12],
+ 'caution-octagon': [16],
+ 'caution-triangle': [16],
+ 'info-circle': [16],
+ 'magic-wand': [20],
+ 'magnifying-glass': [20],
+ 'minus-circle': [12],
+ mobile: [12],
+ box: [12],
+ desktop: [12],
+ bug: [12],
+ posts: [20],
+ rocket: [20],
+ tools: [20],
+ 'triangle-down': [12],
+ 'triangle-right': [12],
+ 'chevron-right': [8],
+ 'chevron-down': [8],
+ star: [16],
+ 'star-outline': [16],
+ },
- let requestedSize: number = size;
- if (!AVAILABLE_SIZES.includes(size)) {
- // find the next largest size
- const possibleSize: ?number = AVAILABLE_SIZES.find(size => {
- return size > requestedSize;
- });
-
- // set to largest size if the real size is larger than what we have
- if (possibleSize == null) {
- requestedSize = Math.max(...AVAILABLE_SIZES);
- } else {
- requestedSize = possibleSize;
+ // $FlowFixMe: not using flow in this file
+ getIconURL(name, size, density) {
+ if (name.indexOf('/') > -1) {
+ return name;
}
- }
- let requestedScale: number =
- typeof window !== 'undefined' ? window.devicePixelRatio : 1;
+ let requestedSize = size;
+ if (!AVAILABLE_SIZES.includes(size)) {
+ // find the next largest size
+ const possibleSize = AVAILABLE_SIZES.find(size => {
+ return size > requestedSize;
+ });
- if (!SCALE.includes(requestedScale)) {
- // find the next largest size
- const possibleScale: ?number = SCALE.find(scale => {
- return scale > requestedScale;
- });
-
- // set to largest size if the real size is larger than what we have
- if (possibleScale == null) {
- requestedScale = Math.max(...SCALE);
- } else {
- requestedScale = possibleScale;
+ // set to largest size if the real size is larger than what we have
+ if (possibleSize == null) {
+ requestedSize = Math.max(...AVAILABLE_SIZES);
+ } else {
+ requestedSize = possibleSize;
+ }
}
- }
- return `https://external.xx.fbcdn.net/assets/?name=${name}&variant=${variant}&size=${requestedSize}&set=facebook_icons&density=${requestedScale}x`;
-}
+ if (!DENSITIES.includes(density)) {
+ // find the next largest size
+ const possibleDensity = DENSITIES.find(scale => {
+ return scale > density;
+ });
+
+ // set to largest size if the real size is larger than what we have
+ if (possibleDensity == null) {
+ density = Math.max(...DENSITIES);
+ } else {
+ density = possibleDensity;
+ }
+ }
+
+ let variant = 'filled';
+ if (name.endsWith('-outline')) {
+ name = name.replace('-outline', '');
+ variant = 'outline';
+ }
+
+ const localPath = path.join('icons', `${name}-${size}@${density}x.png`);
+ // resolve icon locally if possible
+ if (
+ remote &&
+ fs.existsSync(path.join(remote.app.getAppPath(), localPath))
+ ) {
+ return localPath;
+ }
+ return `https://external.xx.fbcdn.net/assets/?name=${name}&variant=${variant}&size=${requestedSize}&set=facebook_icons&density=${density}x`;
+ },
+};
diff --git a/static/serviceWorker.js b/static/serviceWorker.js
deleted file mode 100644
index db48e3abe..000000000
--- a/static/serviceWorker.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright 2018-present Facebook.
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- * @format
- */
-
-const CACHE_NAME = 'v1';
-
-self.addEventListener('message', e => {
- if (e.data.precachedIcons) {
- caches.open(CACHE_NAME).then(cache => cache.addAll(e.data.precachedIcons));
- }
-});
-
-self.addEventListener('fetch', function(event) {
- if (event.request.url.startsWith('https://external.xx.fbcdn.net/assets/')) {
- event.respondWith(
- // Cache falling back to the network
- caches.match(event.request).then(cacheResponse => {
- return (
- cacheResponse ||
- fetch(event.request).then(response => {
- const clone = response.clone();
- // write to cache
- caches
- .open(CACHE_NAME)
- .then(cache => cache.put(event.request, clone));
- return response;
- })
- );
- }),
- );
- }
-});
diff --git a/tsconfig.json b/tsconfig.json
index 08217bba6..44f675aea 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"module": "system",
+ "lib": ["es7", "dom"],
"removeComments": true,
"preserveConstEnums": true,
"outFile": "../../built/local/tsc.js",