Files
flipper/desktop/flipper-ui-core/src/utils/icons.ts
Michel Weststrate bca169df73 Move settings, launcherSettings, GKs to app / flipper-server-core
Summary:
This diff moves a lot of stuff from the client to the server. This diff is fairly large, as a lot of concept closely relate, although some things have split off to the earlier diffs in the stack, or are still to follow (like making intern requests).

This diff primarily moves reading and storing settings and GKs from client to server (both flipper and launcher settings). This means that settings are no longer persisted by Redux (which only exists on client). Most other changes are fallout from that. For now settings are just one big object, although we might need to separate settings that are only make sense in an Electron context. For example launcher settings.

Reviewed By: passy, aigoncharov

Differential Revision: D32498649

fbshipit-source-id: d842faf7a7f03774b621c7656e53a9127afc6192
2021-12-08 04:30:54 -08:00

168 lines
5.0 KiB
TypeScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
// We should get rid of sync use entirely but until then the
// methods are marked as such.
/* eslint-disable node/no-sync */
import fs from 'fs';
import path from 'path';
import {getRenderHostInstance} from '../RenderHost';
import {getStaticPath} from './pathUtils';
const AVAILABLE_SIZES = [8, 10, 12, 16, 18, 20, 24, 32];
const DENSITIES = [1, 1.5, 2, 3, 4];
function getIconsPath() {
return getStaticPath('icons.json');
}
export type Icons = {
[key: string]: number[];
};
let _icons: Icons | undefined;
export function getIconsSync(): Icons {
return (
_icons! ??
(_icons = JSON.parse(fs.readFileSync(getIconsPath(), {encoding: 'utf8'})))
);
}
// Takes a string like 'star', or 'star-outline', and converts it to
// {trimmedName: 'star', variant: 'filled'} or {trimmedName: 'star', variant: 'outline'}
function getIconPartsFromName(icon: string): {
trimmedName: string;
variant: 'outline' | 'filled';
} {
const isOutlineVersion = icon.endsWith('-outline');
const trimmedName = isOutlineVersion ? icon.replace('-outline', '') : icon;
const variant = isOutlineVersion ? 'outline' : 'filled';
return {trimmedName: trimmedName, variant: variant};
}
function getIconFileName(
icon: {trimmedName: string; variant: 'outline' | 'filled'},
size: number,
density: number,
) {
return `${icon.trimmedName}-${icon.variant}-${size}@${density}x.png`;
}
export function buildLocalIconPath(
name: string,
size: number,
density: number,
) {
const icon = getIconPartsFromName(name);
return path.join('icons', getIconFileName(icon, size, density));
}
export function buildLocalIconURL(name: string, size: number, density: number) {
const icon = getIconPartsFromName(name);
return `icons/${getIconFileName(icon, size, density)}`;
}
export function buildIconURLSync(name: string, size: number, density: number) {
const icon = getIconPartsFromName(name);
// eslint-disable-next-line prettier/prettier
const url = `https://facebook.com/assets/?name=${icon.trimmedName}&variant=${icon.variant}&size=${size}&set=facebook_icons&density=${density}x`;
if (
typeof window !== 'undefined' &&
(!getIconsSync()[name] || !getIconsSync()[name].includes(size))
) {
// From utils/isProduction
const isProduction = !/node_modules[\\/]electron[\\/]/.test(
getRenderHostInstance().serverConfig.paths.execPath,
);
if (!isProduction) {
const existing = getIconsSync()[name] || (getIconsSync()[name] = []);
if (!existing.includes(size)) {
// Check if that icon actually exists!
fetch(url)
.then((res) => {
if (res.status === 200 && !existing.includes(size)) {
// the icon exists
existing.push(size);
existing.sort();
fs.writeFileSync(
getIconsPath(),
JSON.stringify(getIconsSync(), null, 2),
'utf8',
);
console.warn(
`Added uncached icon "${name}: [${size}]" to /static/icons.json. Restart Flipper to apply the change.`,
);
} else {
throw new Error(
// eslint-disable-next-line prettier/prettier
`Trying to use icon '${name}' with size ${size} and density ${density}, however the icon doesn't seem to exists at ${url}: ${res.status}`,
);
}
})
.catch((e) => console.error(e));
}
} else {
console.warn(
`Using uncached icon: "${name}: [${size}]". Add it to /static/icons.json to preload it.`,
);
}
}
return url;
}
export function getIconURLSync(
name: string,
size: number,
density: number,
basePath: string = getRenderHostInstance().serverConfig.paths.appPath,
) {
if (name.indexOf('/') > -1) {
return name;
}
let requestedSize = size;
if (!AVAILABLE_SIZES.includes(size)) {
// find the next largest size
const possibleSize = 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;
}
}
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;
}
}
// resolve icon locally if possible
const iconPath = path.join(basePath, buildLocalIconPath(name, size, density));
if (fs.existsSync(iconPath)) {
return buildLocalIconURL(name, size, density);
}
return buildIconURLSync(name, requestedSize, density);
}