Remove crypto dependency

Summary:
Remove crypto dep, which was only used by NUX, to hash the elements that has been confirmed.

Sadly trickier than hoped; there is no uniform api in both browser and Node available that can take a sha-256 hash, and the browser APIs are async.

Reviewed By: aigoncharov

Differential Revision: D32721204

fbshipit-source-id: 32625f83bf6c60cedc4fb7096240c2fa0d8434a7
This commit is contained in:
Michel Weststrate
2021-12-08 04:25:28 -08:00
committed by Facebook GitHub Bot
parent 058785a509
commit f5f9608098
3 changed files with 72 additions and 37 deletions

View File

@@ -7,7 +7,13 @@
* @format * @format
*/ */
import React, {createContext, useCallback, useContext} from 'react'; import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import {Badge, Tooltip, Typography, Button} from 'antd'; import {Badge, Tooltip, Typography, Button} from 'antd';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import {keyframes} from '@emotion/css'; import {keyframes} from '@emotion/css';
@@ -17,11 +23,11 @@ import {createState, useValue} from '../state/atom';
import {SandyDevicePluginInstance} from '../plugin/DevicePlugin'; import {SandyDevicePluginInstance} from '../plugin/DevicePlugin';
import {Layout} from './Layout'; import {Layout} from './Layout';
import {BulbTwoTone} from '@ant-design/icons'; import {BulbTwoTone} from '@ant-design/icons';
import {createHash} from 'crypto';
import type {TooltipPlacement} from 'antd/lib/tooltip'; import type {TooltipPlacement} from 'antd/lib/tooltip';
import {SandyPluginInstance} from '../plugin/Plugin'; import {SandyPluginInstance} from '../plugin/Plugin';
import {theme} from './theme'; import {theme} from './theme';
import {Tracked} from './Tracked'; import {Tracked} from './Tracked';
import {sha256} from '../utils/sha256';
const {Text} = Typography; const {Text} = Typography;
@@ -29,13 +35,12 @@ type NuxManager = ReturnType<typeof createNuxManager>;
const storageKey = `FLIPPER_NUX_STATE`; const storageKey = `FLIPPER_NUX_STATE`;
export function getNuxKey( export async function getNuxKey(
elem: React.ReactNode, elem: React.ReactNode,
currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance, currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance,
) { ): Promise<string> {
return `${currentPlugin?.definition.id ?? 'flipper'}:${createHash('sha256') const hash = await sha256(reactElementToJSXString(elem));
.update(reactElementToJSXString(elem)) return `${currentPlugin?.definition.id ?? 'flipper'}:${hash}`;
.digest('base64')}`;
} }
export function createNuxManager() { export function createNuxManager() {
@@ -52,18 +57,18 @@ export function createNuxManager() {
} }
return { return {
markRead( async markRead(
elem: React.ReactNode, elem: React.ReactNode,
currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance, currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance,
): void { ): Promise<void> {
readMap[getNuxKey(elem, currentPlugin)] = true; readMap[await getNuxKey(elem, currentPlugin)] = true;
save(); save();
}, },
isRead( async isRead(
elem: React.ReactNode, elem: React.ReactNode,
currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance, currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance,
): boolean { ): Promise<boolean> {
return !!readMap[getNuxKey(elem, currentPlugin)]; return !!readMap[await getNuxKey(elem, currentPlugin)];
}, },
resetHints(): void { resetHints(): void {
readMap = {}; readMap = {};
@@ -74,8 +79,8 @@ export function createNuxManager() {
} }
const stubManager: NuxManager = { const stubManager: NuxManager = {
markRead() {}, async markRead() {},
isRead() { async isRead() {
return true; return true;
}, },
resetHints() {}, resetHints() {},
@@ -100,7 +105,18 @@ export function NUX({
const pluginInstance = useContext(SandyPluginContext); const pluginInstance = useContext(SandyPluginContext);
// changing the ticker will force `isRead` to be recomputed // changing the ticker will force `isRead` to be recomputed
const _tick = useValue(manager.ticker); const _tick = useValue(manager.ticker);
const isRead = manager.isRead(title, pluginInstance); // start with Read = true until proven otherwise, to avoid Nux glitches
const [isRead, setIsRead] = useState(true);
useEffect(() => {
manager
.isRead(title, pluginInstance)
.then(setIsRead)
.catch((e) => {
console.warn('Failed to read NUX status', e);
});
}, [manager, title, pluginInstance, _tick]);
const dismiss = useCallback(() => { const dismiss = useCallback(() => {
manager.markRead(title, pluginInstance); manager.markRead(title, pluginInstance);
}, [title, manager, pluginInstance]); }, [title, manager, pluginInstance]);

View File

@@ -11,25 +11,23 @@ import {TestUtils} from '../../';
import React from 'react'; import React from 'react';
import {getNuxKey} from '../NUX'; import {getNuxKey} from '../NUX';
test('nuxkey computation', () => { test('nuxkey computation', async () => {
expect(getNuxKey('test')).toMatchInlineSnapshot( // Not a very good test, as our hashing api's are not available in Node...
`"flipper:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="`, expect(await getNuxKey('test')).toMatchInlineSnapshot(`"flipper:test"`);
); expect(await getNuxKey('test2')).toMatchInlineSnapshot(`"flipper:test2"`);
expect(getNuxKey('test')).toMatchInlineSnapshot( expect(await getNuxKey(<div>bla</div>)).toMatchInlineSnapshot(`
`"flipper:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="`, "flipper:<div>
); bla
expect(getNuxKey('test2')).toMatchInlineSnapshot( </div>"
`"flipper:YDA64iuZiGG847KPM+7BvnWKITyGyTwHbb6fVYwRx1I="`, `);
); expect(await getNuxKey(<div>bla2</div>)).toMatchInlineSnapshot(`
expect(getNuxKey(<div>bla</div>)).toMatchInlineSnapshot( "flipper:<div>
`"flipper:myN0Mqqzs3fPwYDKGEQVG9XD9togJNWYJiy1VNQOf18="`, bla2
); </div>"
expect(getNuxKey(<div>bla2</div>)).toMatchInlineSnapshot( `);
`"flipper:B6kICeYCJMWeUThs5TWCLuiwCqzr5cWn67xXA4ET0bU="`,
);
}); });
test('nuxkey computation with plugin', () => { test('nuxkey computation with plugin', async () => {
const res = TestUtils.startPlugin({ const res = TestUtils.startPlugin({
Component() { Component() {
return null; return null;
@@ -40,8 +38,6 @@ test('nuxkey computation with plugin', () => {
}); });
expect( expect(
getNuxKey('test', (res as any)._backingInstance), await getNuxKey('test', (res as any)._backingInstance),
).toMatchInlineSnapshot( ).toMatchInlineSnapshot(`"TestPlugin:test"`);
`"TestPlugin:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="`,
);
}); });

View File

@@ -0,0 +1,23 @@
/**
* 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
*/
export function sha256(message: string): Promise<string> {
if (process.env.NODE_ENV === 'test') {
return Promise.resolve(message.substr(0, 100));
}
// From https://stackoverflow.com/a/48161723/1983583
const msgBuffer = new TextEncoder().encode(message);
return crypto.subtle.digest('SHA-256', msgBuffer).then((hashBuffer) => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
return hashHex;
});
}