diff --git a/package.json b/package.json index ea5cedb8a..7993daffd 100644 --- a/package.json +++ b/package.json @@ -51,10 +51,10 @@ }, "devDependencies": { "@jest-runner/electron": "^2.0.1", - "@types/react-dom": "^16.8.5", "@types/invariant": "^2.2.30", "@types/jest": "^24.0.16", "@types/react": "^16.8.24", + "@types/react-dom": "^16.8.5", "@types/react-redux": "^7.1.1", "@types/redux-persist": "^4.3.1", "@types/rsocket-core": "^0.0.2", @@ -84,6 +84,7 @@ "typescript": "^3.5.2" }, "dependencies": { + "@types/redux-devtools-extension": "^2.13.2", "@types/rsocket-tcp-server": "^0.0.2", "JSONStream": "^1.3.1", "adbkit-fb": "2.10.1", diff --git a/scripts/build-release.js b/scripts/build-release.js index df2047e4f..7672f1702 100755 --- a/scripts/build-release.js +++ b/scripts/build-release.js @@ -160,7 +160,7 @@ function downloadIcons(buildFolder) { copyStaticFolder(dir); await downloadIcons(dir); await compileDefaultPlugins(path.join(dir, 'defaultPlugins')); - await compile(dir, path.join(__dirname, '..', 'src', 'init.js')); + await compile(dir, path.join(__dirname, '..', 'src', 'init.tsx')); const versionNumber = getVersionNumber(); const hgRevision = await genMercurialRevision(); modifyPackageManifest(dir, versionNumber, hgRevision); diff --git a/src/App.js b/src/App.tsx similarity index 71% rename from src/App.js rename to src/App.tsx index 8edf3a80a..d4c386a65 100644 --- a/src/App.js +++ b/src/App.tsx @@ -8,22 +8,22 @@ import React from 'react'; import {FlexColumn, FlexRow} from 'flipper'; import {connect} from 'react-redux'; -import WelcomeScreen from './chrome/WelcomeScreen.tsx'; -import TitleBar from './chrome/TitleBar.tsx'; -import MainSidebar from './chrome/MainSidebar.tsx'; -import BugReporterDialog from './chrome/BugReporterDialog.tsx'; -import ErrorBar from './chrome/ErrorBar.tsx'; -import ShareSheet from './chrome/ShareSheet.tsx'; -import SignInSheet from './chrome/SignInSheet.tsx'; -import ExportDataPluginSheet from './chrome/ExportDataPluginSheet.tsx'; -import ShareSheetExportFile from './chrome/ShareSheetExportFile.tsx'; -import PluginContainer from './PluginContainer.js'; -import Sheet from './chrome/Sheet.tsx'; +import WelcomeScreen from './chrome/WelcomeScreen'; +import TitleBar from './chrome/TitleBar'; +import MainSidebar from './chrome/MainSidebar'; +import BugReporterDialog from './chrome/BugReporterDialog'; +import ErrorBar from './chrome/ErrorBar'; +import ShareSheet from './chrome/ShareSheet'; +import SignInSheet from './chrome/SignInSheet'; +import ExportDataPluginSheet from './chrome/ExportDataPluginSheet'; +import ShareSheetExportFile from './chrome/ShareSheetExportFile'; +import PluginContainer from './PluginContainer'; +import Sheet from './chrome/Sheet'; import {ipcRenderer, remote} from 'electron'; -import PluginDebugger from './chrome/PluginDebugger.tsx'; +import PluginDebugger from './chrome/PluginDebugger'; import { - ShareType, ActiveSheet, + ShareType, ACTIVE_SHEET_BUG_REPORTER, ACTIVE_SHEET_PLUGIN_DEBUGGER, ACTIVE_SHEET_SHARE_DATA, @@ -31,25 +31,27 @@ import { ACTIVE_SHEET_SHARE_DATA_IN_FILE, ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT, ACTIVE_SHEET_PLUGIN_SHEET, -} from './reducers/application.tsx'; -import type {Logger} from './fb-interfaces/Logger.js'; -import type BugReporter from './fb-stubs/BugReporter.tsx'; -import type BaseDevice from './devices/BaseDevice.tsx'; +} from './reducers/application'; +import {Logger} from './fb-interfaces/Logger.js'; +import BugReporter from './fb-stubs/BugReporter'; +import BaseDevice from './devices/BaseDevice'; +import {State as Store} from './reducers/index'; const version = remote.app.getVersion(); -type OwnProps = {| - logger: Logger, - bugReporter: BugReporter, -|}; +type OwnProps = { + logger: Logger; + bugReporter: BugReporter; +}; -type Props = {| - ...OwnProps, - leftSidebarVisible: boolean, - selectedDevice: ?BaseDevice, - error: ?string, - activeSheet: ActiveSheet, - share: ?ShareType, -|}; +type StateFromProps = { + leftSidebarVisible: boolean; + selectedDevice: BaseDevice | undefined; + error: string | null | undefined; + activeSheet: ActiveSheet; + share: ShareType | undefined; +}; + +type Props = StateFromProps & OwnProps; export class App extends React.Component { componentDidMount() { @@ -67,7 +69,7 @@ export class App extends React.Component { ipcRenderer.send('componentDidMount'); } - getSheet = (onHide: () => mixed) => { + getSheet = (onHide: () => any) => { const {activeSheet} = this.props; switch (activeSheet) { case ACTIVE_SHEET_BUG_REPORTER: @@ -124,7 +126,7 @@ export class App extends React.Component { } } -export default connect( +export default connect( ({ application: {leftSidebarVisible, activeSheet, share}, connections: {selectedDevice, error}, diff --git a/src/MenuBar.js b/src/MenuBar.tsx similarity index 84% rename from src/MenuBar.js rename to src/MenuBar.tsx index 12be08993..bea59a330 100644 --- a/src/MenuBar.js +++ b/src/MenuBar.tsx @@ -5,39 +5,25 @@ * @format */ -import type {FlipperPlugin, FlipperDevicePlugin} from './plugin.tsx'; -import {showOpenDialog} from './utils/exportData.tsx'; -import { - setExportDataToFileActiveSheet, - setActiveSheet, - setSelectPluginsToExportActiveSheet, - ACTIVE_SHEET_SHARE_DATA, -} from './reducers/application.tsx'; -import type {Store} from './reducers/index.tsx'; -import electron from 'electron'; -import {constants} from 'flipper'; -export type DefaultKeyboardAction = 'clear' | 'goToBottom' | 'createPaste'; -export type TopLevelMenu = 'Edit' | 'View' | 'Window' | 'Help'; -const {dialog} = electron.remote; +import {FlipperPlugin, FlipperDevicePlugin} from './plugin'; +import {showOpenDialog} from './utils/exportData'; +import {setSelectPluginsToExportActiveSheet} from './reducers/application'; +import {Store} from './reducers/'; +import electron, {MenuItemConstructorOptions} from 'electron'; +import constants from './fb-stubs/constants'; import os from 'os'; import path from 'path'; -type MenuItem = {| - label?: string, - accelerator?: string, - role?: string, - click?: Function, - submenu?: Array, - type?: string, - enabled?: boolean, -|}; +export type DefaultKeyboardAction = 'clear' | 'goToBottom' | 'createPaste'; +export type TopLevelMenu = 'Edit' | 'View' | 'Window' | 'Help'; +const {dialog} = electron.remote; -export type KeyboardAction = {| - action: string, - label: string, - accelerator?: string, - topLevelMenu: TopLevelMenu, -|}; +export type KeyboardAction = { + action: string; + label: string; + accelerator?: string; + topLevelMenu: TopLevelMenu; +}; const defaultKeyboardActions: Array = [ { @@ -61,7 +47,7 @@ const defaultKeyboardActions: Array = [ export type KeyboardActions = Array; -const menuItems: Map = new Map(); +const menuItems: Map = new Map(); let pluginActionHandler; function actionHandler(action: string) { @@ -73,7 +59,7 @@ function actionHandler(action: string) { } export function setupMenuBar( - plugins: Array | FlipperDevicePlugin<>>>, + plugins: Array, store: Store, ) { const template = getTemplate( @@ -82,12 +68,9 @@ export function setupMenuBar( store, ); // collect all keyboard actions from all plugins - const registeredActions: Set = new Set( + const registeredActions: Set = new Set( plugins - .map( - (plugin: Class | FlipperDevicePlugin<>>) => - plugin.keyboardActions || [], - ) + .map(plugin => plugin.keyboardActions || []) .reduce((acc: KeyboardActions, cv) => acc.concat(cv), []) .map((action: DefaultKeyboardAction | KeyboardAction) => typeof action === 'string' @@ -130,7 +113,7 @@ export function setupMenuBar( function appendMenuItem( template: Array, actionHandler: (action: string) => void, - item: ?KeyboardAction, + item: KeyboardAction, ) { const keyboardAction = item; if (keyboardAction == null) { @@ -140,7 +123,7 @@ function appendMenuItem( menu => menu.label === keyboardAction.topLevelMenu, ); if (itemIndex > -1 && template[itemIndex].submenu != null) { - template[itemIndex].submenu.push({ + (template[itemIndex].submenu as MenuItemConstructorOptions[]).push({ click: () => actionHandler(keyboardAction.action), label: keyboardAction.label, accelerator: keyboardAction.accelerator, @@ -150,7 +133,9 @@ function appendMenuItem( } export function activateMenuItems( - activePlugin: FlipperPlugin<> | FlipperDevicePlugin<>, + activePlugin: + | FlipperPlugin + | FlipperDevicePlugin, ) { // disable all keyboard actions for (const item of menuItems) { @@ -183,15 +168,15 @@ export function activateMenuItems( } function getTemplate( - app: Object, - shell: Object, + app: electron.App, + shell: electron.Shell, store: Store, ): Array { const exportSubmenu = [ { label: 'File...', accelerator: 'CommandOrControl+E', - click: function(item: Object, focusedWindow: Object) { + click: function() { dialog.showSaveDialog( null, { @@ -214,20 +199,20 @@ function getTemplate( exportSubmenu.push({ label: 'Sharable Link', accelerator: 'CommandOrControl+Shift+E', - click: async function(item: Object, focusedWindow: Object) { + click: function() { store.dispatch(setSelectPluginsToExportActiveSheet({type: 'link'})); }, }); } - const template = [ + const template: MenuItemConstructorOptions[] = [ { label: 'File', submenu: [ { label: 'Open File...', accelerator: 'CommandOrControl+O', - click: function(item: Object, focusedWindow: Object) { + click: function() { showOpenDialog(store); }, }, @@ -281,7 +266,10 @@ function getTemplate( { label: 'Reload', accelerator: 'CmdOrCtrl+R', - click: function(item: Object, focusedWindow: Object) { + click: function( + _, + focusedWindow: electron.BrowserWindow | undefined, + ) { if (focusedWindow) { focusedWindow.reload(); } @@ -296,7 +284,10 @@ function getTemplate( return 'F11'; } })(), - click: function(item: Object, focusedWindow: Object) { + click: function( + _, + focusedWindow: electron.BrowserWindow | undefined, + ) { if (focusedWindow) { focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); } @@ -311,8 +302,12 @@ function getTemplate( return 'Ctrl+Shift+I'; } })(), - click: function(item: Object, focusedWindow: Object) { + click: function( + _, + focusedWindow: electron.BrowserWindow | undefined, + ) { if (focusedWindow) { + // @ts-ignore: https://github.com/electron/electron/issues/7832 focusedWindow.toggleDevTools(); } }, @@ -414,11 +409,11 @@ function getTemplate( }, ], }); - const windowMenu = template.find(function(m: Object) { + const windowMenu = template.find(function(m) { return m.role === 'window'; }); if (windowMenu) { - windowMenu.submenu.push( + (windowMenu.submenu as MenuItemConstructorOptions[]).push( { type: 'separator', }, diff --git a/src/NotificationsHub.js b/src/NotificationsHub.tsx similarity index 85% rename from src/NotificationsHub.js rename to src/NotificationsHub.tsx index 5efaa5bfc..e1e7a779a 100644 --- a/src/NotificationsHub.js +++ b/src/NotificationsHub.tsx @@ -5,17 +5,9 @@ * @format */ -import type { - SearchableProps, - FlipperBasePlugin, - FlipperPlugin, - Device, -} from 'flipper'; -import type {PluginNotification} from './reducers/notifications.tsx'; -import type {Logger} from './fb-interfaces/Logger'; - +import {SearchableProps, FlipperBasePlugin, FlipperPlugin} from 'flipper'; +import {Logger} from './fb-interfaces/Logger'; import { - FlipperDevicePlugin, Searchable, Button, ButtonGroup, @@ -27,30 +19,38 @@ import { styled, colors, } from 'flipper'; +import {FlipperDevicePlugin, BaseAction} from './plugin'; import {connect} from 'react-redux'; import React, {Component, Fragment} from 'react'; import {clipboard} from 'electron'; import PropTypes from 'prop-types'; import { + PluginNotification, clearAllNotifications, updatePluginBlacklist, updateCategoryBlacklist, -} from './reducers/notifications.tsx'; -import {selectPlugin} from './reducers/connections.tsx'; -import {textContent} from './utils/index.tsx'; -import createPaste from './fb-stubs/createPaste.tsx'; +} from './reducers/notifications'; +import {selectPlugin} from './reducers/connections'; +import {State as Store} from './reducers/index'; +import textContent from './utils/textContent'; +import createPaste from './fb-stubs/createPaste'; +import {KeyboardActions} from './MenuBar'; -export default class Notifications extends FlipperDevicePlugin<{}> { +export default class Notifications< + S, + A extends BaseAction, + P +> extends FlipperDevicePlugin { static id = 'notifications'; static title = 'Notifications'; static icon = 'bell'; - static keyboardActions = ['clear']; + static keyboardActions: KeyboardActions = ['clear']; static contextTypes = { store: PropTypes.object.isRequired, }; - static supportsDevice(device: Device) { + static supportsDevice() { return false; } @@ -99,33 +99,36 @@ export default class Notifications extends FlipperDevicePlugin<{}> { } } -type OwnProps = {| - ...SearchableProps, - onClear: () => void, - selectedID: ?string, - logger: Logger, -|}; +type OwnProps = { + onClear: () => void; + selectedID: string | null | undefined; + logger: Logger; +} & SearchableProps; -type Props = {| - ...OwnProps, - activeNotifications: Array, - invalidatedNotifications: Array, - blacklistedPlugins: Array, - blacklistedCategories: Array, - devicePlugins: Map>>, - clientPlugins: Map>>, - updatePluginBlacklist: (blacklist: Array) => mixed, - updateCategoryBlacklist: (blacklist: Array) => mixed, - selectPlugin: (payload: {| - selectedPlugin: ?string, - selectedApp: ?string, - deepLinkPayload: ?string, - |}) => mixed, -|}; +type StateFromProps = { + activeNotifications: Array; + invalidatedNotifications: Array; + blacklistedPlugins: Array; + blacklistedCategories: Array; + devicePlugins: Map; + clientPlugins: Map; +}; -type State = {| - selectedNotification: ?string, -|}; +type DispatchFromProps = { + selectPlugin: (payload: { + selectedPlugin: string | null | undefined; + selectedApp: string | null | undefined; + deepLinkPayload: string | null | undefined; + }) => any; + updatePluginBlacklist: (blacklist: Array) => any; + updateCategoryBlacklist: (blacklist: Array) => any; +}; + +type Props = OwnProps & StateFromProps & DispatchFromProps; + +type State = { + selectedNotification: string | null | undefined; +}; const Content = styled(FlexColumn)({ padding: '0 10px', @@ -327,7 +330,12 @@ class NotificationsTable extends Component { } } -const ConnectedNotificationsTable = connect( +const ConnectedNotificationsTable = connect< + StateFromProps, + DispatchFromProps, + OwnProps, + Store +>( ({ notifications: { activeNotifications, @@ -351,7 +359,10 @@ const ConnectedNotificationsTable = connect( }, )(Searchable(NotificationsTable)); -const shadow = (props, hover) => { +const shadow = ( + props: {isSelected?: boolean; inactive?: boolean}, + _hover?: boolean, +) => { if (props.inactive) { return `inset 0 0 0 1px ${colors.light10}`; } @@ -448,27 +459,30 @@ const NotificationButton = styled('div')({ }); type ItemProps = { - ...PluginNotification, - onHighlight?: () => mixed, - onHidePlugin?: () => mixed, - onHideCategory?: () => mixed, - isSelected?: boolean, - inactive?: boolean, - selectPlugin?: (payload: {| - selectedPlugin: ?string, - selectedApp: ?string, - deepLinkPayload: ?string, - |}) => mixed, - logger?: Logger, - plugin: ?Class>, + onHighlight?: () => any; + onHidePlugin?: () => any; + onHideCategory?: () => any; + onClear?: () => any; + isSelected?: boolean; + inactive?: boolean; + selectPlugin?: (payload: { + selectedPlugin: string | null | undefined; + selectedApp: string | null | undefined; + deepLinkPayload: string | null | undefined; + }) => any; + logger?: Logger; + plugin: typeof FlipperBasePlugin | null | undefined; }; -type ItemState = {| - reportedNotHelpful: boolean, -|}; +type ItemState = { + reportedNotHelpful: boolean; +}; -class NotificationItem extends Component { - constructor(props: ItemProps) { +class NotificationItem extends Component< + ItemProps & PluginNotification, + ItemState +> { + constructor(props: ItemProps & PluginNotification) { super(props); const items = []; if (props.onHidePlugin && props.plugin) { @@ -566,7 +580,7 @@ class NotificationItem extends Component { isSelected={isSelected} inactive={inactive} items={this.contextMenuItems}> - + {notification.title} {notification.message} diff --git a/src/PluginContainer.js b/src/PluginContainer.tsx similarity index 67% rename from src/PluginContainer.js rename to src/PluginContainer.tsx index 5ca93d566..6ae3d2240 100644 --- a/src/PluginContainer.js +++ b/src/PluginContainer.tsx @@ -4,27 +4,30 @@ * LICENSE file in the root directory of this source tree. * @format */ -import type {FlipperPlugin, FlipperDevicePlugin} from './plugin.tsx'; -import type {Logger} from './fb-interfaces/Logger'; -import type {Props as PluginProps} from './plugin.tsx'; -import {pluginKey as getPluginKey} from './reducers/pluginStates.tsx'; -import Client from './Client.tsx'; -import BaseDevice from './devices/BaseDevice.tsx'; +import { + FlipperPlugin, + FlipperDevicePlugin, + Props as PluginProps, +} from './plugin'; +import {Logger} from './fb-interfaces/Logger'; +import BaseDevice from './devices/BaseDevice'; +import {pluginKey as getPluginKey} from './reducers/pluginStates'; +import Client from './Client'; import { ErrorBoundary, - PureComponent, FlexColumn, FlexRow, colors, styled, ArchivedDevice, } from 'flipper'; -import React from 'react'; +import React, {PureComponent} from 'react'; import {connect} from 'react-redux'; -import {setPluginState} from './reducers/pluginStates.tsx'; -import {selectPlugin} from './reducers/connections.tsx'; +import {setPluginState} from './reducers/pluginStates'; +import {selectPlugin} from './reducers/connections'; +import {State as Store} from './reducers/index'; import NotificationsHub from './NotificationsHub'; -import {activateMenuItems} from './MenuBar.js'; +import {activateMenuItems} from './MenuBar'; const Container = styled(FlexColumn)({ width: 0, @@ -39,34 +42,48 @@ const SidebarContainer = styled(FlexRow)({ overflow: 'scroll', }); -type OwnProps = {| - logger: Logger, -|}; +type OwnProps = { + logger: Logger; +}; -type Props = {| - ...OwnProps, - pluginState: Object, - activePlugin: ?Class | FlipperDevicePlugin<>>, - target: Client | BaseDevice | null, - pluginKey: ?string, - deepLinkPayload: ?string, - selectedApp: ?string, - selectPlugin: (payload: {| - selectedPlugin: ?string, - selectedApp?: ?string, - deepLinkPayload: ?string, - |}) => mixed, +type StateFromProps = { + pluginState: Object; + activePlugin: typeof FlipperPlugin | typeof FlipperDevicePlugin; + target: Client | BaseDevice | null; + pluginKey: string | null | undefined; + deepLinkPayload: string | null | undefined; + selectedApp: string | null | undefined; + isArchivedDevice: boolean; +}; + +type DispatchFromProps = { + selectPlugin: (payload: { + selectedPlugin: string | null | undefined; + selectedApp?: string | null | undefined; + deepLinkPayload: string | null | undefined; + }) => any; setPluginState: (payload: { - pluginKey: string, - state: Object, - }) => void, - isArchivedDevice: boolean, -|}; + pluginKey: string; + state: Partial; + }) => void; +}; + +type Props = StateFromProps & DispatchFromProps & OwnProps; class PluginContainer extends PureComponent { - plugin: ?FlipperPlugin<> | FlipperDevicePlugin<>; + plugin: + | FlipperPlugin + | FlipperDevicePlugin + | null + | undefined; - refChanged = (ref: ?FlipperPlugin<> | FlipperDevicePlugin<>) => { + refChanged = ( + ref: + | FlipperPlugin + | FlipperDevicePlugin + | null + | undefined, + ) => { if (this.plugin) { this.plugin._teardown(); this.plugin = null; @@ -100,7 +117,16 @@ class PluginContainer extends PureComponent { console.warn(`No selected plugin. Rendering empty!`); return null; } - const props: PluginProps = { + const props: PluginProps & { + key: string; + ref: ( + ref: + | FlipperPlugin + | FlipperDevicePlugin + | null + | undefined, + ) => void; + } = { key: pluginKey, logger: this.props.logger, selectedApp, @@ -113,7 +139,10 @@ class PluginContainer extends PureComponent { setPersistedState: state => setPluginState({pluginKey, state}), target, deepLinkPayload: this.props.deepLinkPayload, - selectPlugin: (pluginID: string, deepLinkPayload: ?string) => { + selectPlugin: ( + pluginID: string, + deepLinkPayload: string | null | undefined, + ) => { const {target} = this.props; // check if plugin will be available if ( @@ -148,9 +177,8 @@ class PluginContainer extends PureComponent { } } -export default connect( +export default connect( ({ - application: {rightSidebarVisible, rightSidebarAvailable}, connections: { selectedPlugin, selectedDevice, @@ -163,7 +191,10 @@ export default connect( }) => { let pluginKey = null; let target = null; - let activePlugin: ?Class | FlipperDevicePlugin<>> = null; + let activePlugin: + | typeof FlipperDevicePlugin + | typeof FlipperPlugin + | null = null; if (selectedPlugin) { if (selectedPlugin === NotificationsHub.id) { @@ -185,6 +216,7 @@ export default connect( const isArchivedDevice = !selectedDevice ? false : selectedDevice instanceof ArchivedDevice; + return { pluginState: pluginStates[pluginKey], activePlugin, diff --git a/src/__tests__/App.electron.js b/src/__tests__/App.electron.tsx similarity index 83% rename from src/__tests__/App.electron.js rename to src/__tests__/App.electron.tsx index b99390d54..b6e5b8682 100644 --- a/src/__tests__/App.electron.js +++ b/src/__tests__/App.electron.tsx @@ -5,13 +5,13 @@ * @format */ import React from 'react'; -import {App} from '../App.js'; +import {App} from '../App'; import {Provider} from 'react-redux'; import renderer from 'react-test-renderer'; -import reducers from '../reducers/index.tsx'; +import reducers from '../reducers/index'; import configureStore from 'redux-mock-store'; -import {init as initLogger} from '../fb-stubs/Logger.tsx'; -import BugReporter from '../fb-stubs/BugReporter.tsx'; +import {init as initLogger} from '../fb-stubs/Logger'; +import BugReporter from '../fb-stubs/BugReporter'; // create redux store with initial state const mockStore = configureStore([])(reducers(undefined, {type: 'INIT'})); diff --git a/src/__tests__/__snapshots__/App.electron.js.snap b/src/__tests__/__snapshots__/App.electron.js.snap deleted file mode 100644 index 6f8b678ad..000000000 --- a/src/__tests__/__snapshots__/App.electron.js.snap +++ /dev/null @@ -1,254 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Empty app state matches snapshot 1`] = ` -
-
-
-
- No device selected -
-
-
-
-
-
-
-
-
-
- - 5.0.8-dev - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - Welcome to Flipper - - - Development Mode - -
-
-
- - Using Flipper - - - Learn how Flipper can help you debug your App - -
-
-
-
-
- - Create your own plugin - - - Get started with these pointers - -
-
-
-
-
- - Add Flipper support to your app - - - Get started with these pointers - -
-
-
-
-
- - Contributing and Feedback - - - Report issues and help us improve Flipper - -
-
-
-
-
-
-`; diff --git a/src/__tests__/createTablePlugin.node.js b/src/__tests__/createTablePlugin.node.tsx similarity index 75% rename from src/__tests__/createTablePlugin.node.js rename to src/__tests__/createTablePlugin.node.tsx index 70a9fd737..acac62828 100644 --- a/src/__tests__/createTablePlugin.node.js +++ b/src/__tests__/createTablePlugin.node.tsx @@ -5,31 +5,27 @@ * @format */ -import {createTablePlugin} from '../createTablePlugin.js'; - -import type {TableRows_immutable} from 'flipper'; - -//import type {PersistedState, RowData} from '../createTablePlugin.js'; -import {FlipperPlugin} from '../plugin.tsx'; - +import {createTablePlugin} from '../createTablePlugin'; +import {FlipperPlugin} from '../plugin'; import {List, Map as ImmutableMap} from 'immutable'; +import {TableRows_immutable} from '../ui/components/table/types.js'; const PROPS = { method: 'method', resetMethod: 'resetMethod', columns: {}, columnSizes: {}, - renderSidebar: (r: {id: string}) => {}, - buildRow: (r: {id: string}) => {}, + renderSidebar: () => {}, + buildRow: () => {}, }; -type PersistedState = {| - rows: TableRows_immutable, - datas: ImmutableMap, -|}; +type PersistedState = { + rows: TableRows_immutable; + datas: ImmutableMap; +}; type RowData = { - id: string, + id: string; }; test('createTablePlugin returns FlipperPlugin', () => { @@ -55,8 +51,9 @@ test('persistedStateReducer is resetting data', () => { ]), }; - // $FlowFixMe persistedStateReducer exists for createTablePlugin - const {rows, datas} = tablePlugin.persistedStateReducer(ps, resetMethod, {}); + const {rows, datas} = tablePlugin.persistedStateReducer(ps, resetMethod, { + id: '0', + }); expect(datas.toJSON()).toEqual({}); expect(rows.size).toBe(0); diff --git a/src/chrome/MainSidebar.tsx b/src/chrome/MainSidebar.tsx index 94f0b0a16..a80741d8f 100644 --- a/src/chrome/MainSidebar.tsx +++ b/src/chrome/MainSidebar.tsx @@ -29,7 +29,7 @@ import { LoadingIndicator, } from 'flipper'; import React, {Component, PureComponent} from 'react'; -import NotificationsHub from '../NotificationsHub.js'; +import NotificationsHub from '../NotificationsHub'; import {selectPlugin} from '../reducers/connections'; import {setActiveSheet} from '../reducers/application'; import UserAccount from './UserAccount'; diff --git a/src/createTablePlugin.js b/src/createTablePlugin.tsx similarity index 81% rename from src/createTablePlugin.js rename to src/createTablePlugin.tsx index 96f1c4ded..26c41227e 100644 --- a/src/createTablePlugin.js +++ b/src/createTablePlugin.tsx @@ -5,46 +5,46 @@ * @format */ -import type { +import { TableHighlightedRows, TableRows_immutable, TableColumnSizes, TableColumns, } from 'flipper'; - import FlexColumn from './ui/components/FlexColumn'; import Button from './ui/components/Button'; -import DetailSidebar from './chrome/DetailSidebar.tsx'; -import {FlipperPlugin} from './plugin.tsx'; +import DetailSidebar from './chrome/DetailSidebar'; +import {FlipperPlugin} from './plugin'; import SearchableTable_immutable from './ui/components/searchable/SearchableTable_immutable'; -import textContent from './utils/textContent.tsx'; -import createPaste from './fb-stubs/createPaste.tsx'; - +import textContent from './utils/textContent'; +import createPaste from './fb-stubs/createPaste'; import {List, Map as ImmutableMap} from 'immutable'; +import React from 'react'; +import {KeyboardActions} from './MenuBar'; type ID = string; -export type RowData = { - id: ID, +export interface RowData { + id: ID; +} + +type Props = { + method: string; + resetMethod?: string; + columns: TableColumns; + columnSizes: TableColumnSizes; + renderSidebar: (row: T) => any; + buildRow: (row: T) => any; }; -type Props = {| - method: string, - resetMethod?: string, - columns: TableColumns, - columnSizes: TableColumnSizes, - renderSidebar: (row: T) => any, - buildRow: (row: T) => any, -|}; +export type PersistedState = { + rows: TableRows_immutable; + datas: ImmutableMap; +}; -export type PersistedState = {| - rows: TableRows_immutable, - datas: ImmutableMap, -|}; - -type State = {| - selectedIds: Array, -|}; +type State = { + selectedIds: Array; +}; /** * createTablePlugin creates a Plugin class which handles fetching data from the client and @@ -59,9 +59,10 @@ type State = {| * data provided. This is useful when connecting to Flipper for this first time, or reconnecting to * the client in an unknown state. */ -export function createTablePlugin(props: Props) { - return class extends FlipperPlugin> { - static keyboardActions = ['clear', 'createPaste']; +export function createTablePlugin(props: Props) { + // @ts-ignore + return class extends FlipperPlugin> { + static keyboardActions: KeyboardActions = ['clear', 'createPaste']; static defaultPersistedState = { rows: List(), @@ -69,13 +70,13 @@ export function createTablePlugin(props: Props) { }; static persistedStateReducer = ( - persistedState: PersistedState<*>, + persistedState: PersistedState, method: string, payload: T | Array, - ): $Shape> => { + ): Partial> => { if (method === props.method) { return List(Array.isArray(payload) ? payload : [payload]).reduce( - (ps: PersistedState<*>, data: T) => { + (ps: PersistedState, data: T) => { if (data.id == null) { console.warn('The data sent did not contain an ID.', data); } diff --git a/src/dispatcher/__tests__/deeplinkURLParsing.node.js b/src/dispatcher/__tests__/deeplinkURLParsing.node.js index 6ec32161c..e797478a1 100644 --- a/src/dispatcher/__tests__/deeplinkURLParsing.node.js +++ b/src/dispatcher/__tests__/deeplinkURLParsing.node.js @@ -20,7 +20,6 @@ test('test parsing of deeplink URL when arguments are less', () => { }); test('test parsing of deeplink URL when url is null', () => { - // $FlowFixMe const components = uriComponents(null); expect(components).toEqual([]); }); diff --git a/src/dispatcher/__tests__/plugins.electron.js b/src/dispatcher/__tests__/plugins.electron.js index eb38d1aa9..884d45234 100644 --- a/src/dispatcher/__tests__/plugins.electron.js +++ b/src/dispatcher/__tests__/plugins.electron.js @@ -122,10 +122,7 @@ test('requirePlugin loads plugin', () => { homepage, out: path.join(__dirname, 'TestPlugin.js'), }); - // $FlowFixMe expect(plugin.prototype).toBeInstanceOf(FlipperPlugin); - // $FlowFixMe expect(plugin.homepage).toBe(homepage); - // $FlowFixMe expect(plugin.id).toBe(TestPlugin.id); }); diff --git a/src/dispatcher/plugins.tsx b/src/dispatcher/plugins.tsx index 480b6ba74..5d0f5992f 100644 --- a/src/dispatcher/plugins.tsx +++ b/src/dispatcher/plugins.tsx @@ -9,7 +9,6 @@ import {Store} from '../reducers/index'; import {Logger} from '../fb-interfaces/Logger.js'; import {FlipperPlugin, FlipperDevicePlugin} from '../plugin'; import {State} from '../reducers/plugins'; - import React from 'react'; import ReactDOM from 'react-dom'; import * as Flipper from '../index.js'; @@ -22,7 +21,7 @@ import { import {remote} from 'electron'; import GK from '../fb-stubs/GK'; import {FlipperBasePlugin} from '../plugin'; -import {setupMenuBar} from '../MenuBar.js'; +import {setupMenuBar} from '../MenuBar'; import path from 'path'; import {default as config} from '../utils/processConfig'; import isProduction from '../utils/isProduction'; diff --git a/src/index.js b/src/index.js index d20dd5406..2d4f79e20 100644 --- a/src/index.js +++ b/src/index.js @@ -38,7 +38,7 @@ export { } from './devices/BaseDevice.tsx'; export {shouldParseAndroidLog} from './utils/crashReporterUtility.tsx'; export {default as isProduction} from './utils/isProduction.tsx'; -export {createTablePlugin} from './createTablePlugin.js'; +export {createTablePlugin} from './createTablePlugin.tsx'; export {default as DetailSidebar} from './chrome/DetailSidebar.tsx'; export {default as Device} from './devices/BaseDevice.tsx'; diff --git a/src/init.js b/src/init.tsx similarity index 62% rename from src/init.js rename to src/init.tsx index fd88f165c..13bd7fe47 100644 --- a/src/init.js +++ b/src/init.tsx @@ -8,30 +8,34 @@ import {Provider} from 'react-redux'; import ReactDOM from 'react-dom'; import {useState, useEffect} from 'react'; -import {ContextMenuProvider} from 'flipper'; -import GK from './fb-stubs/GK.tsx'; -import {init as initLogger} from './fb-stubs/Logger.tsx'; -import App from './App.js'; -import BugReporter from './fb-stubs/BugReporter.tsx'; -import setupPrefetcher from './fb-stubs/Prefetcher.tsx'; +import ContextMenuProvider from './ui/components/ContextMenuProvider.js'; +import GK from './fb-stubs/GK'; +import {init as initLogger} from './fb-stubs/Logger'; +import App from './App'; +import BugReporter from './fb-stubs/BugReporter'; +import setupPrefetcher from './fb-stubs/Prefetcher'; import {createStore} from 'redux'; import {persistStore} from 'redux-persist'; -import reducers from './reducers/index.tsx'; -import dispatcher from './dispatcher/index.tsx'; +import reducers, {Actions, State as StoreState} from './reducers/index'; +import dispatcher from './dispatcher/index'; import TooltipProvider from './ui/components/TooltipProvider.js'; -import config from './utils/processConfig.tsx'; -import {stateSanitizer} from './utils/reduxDevToolsConfig.tsx'; -import {initLauncherHooks} from './utils/launcher.tsx'; -import initCrashReporter from './utils/electronCrashReporter.tsx'; -import fbConfig from './fb-stubs/config.tsx'; +import config from './utils/processConfig'; +import {stateSanitizer} from './utils/reduxDevToolsConfig'; +import {initLauncherHooks} from './utils/launcher'; +import initCrashReporter from './utils/electronCrashReporter'; +import fbConfig from './fb-stubs/config'; import {isFBEmployee} from './utils/fbEmployee.js'; -import path from 'path'; -import WarningEmployee from './chrome/WarningEmployee.tsx'; +import WarningEmployee from './chrome/WarningEmployee'; +import React from 'react'; -const store = createStore( +const store = createStore( reducers, - window.__REDUX_DEVTOOLS_EXTENSION__ && - window.__REDUX_DEVTOOLS_EXTENSION__({stateSanitizer}), + window.__REDUX_DEVTOOLS_EXTENSION__ + ? window.__REDUX_DEVTOOLS_EXTENSION__({ + // @ts-ignore + stateSanitizer, + }) + : undefined, ); const logger = initLogger(store); @@ -55,7 +59,7 @@ const AppFrame = () => { {warnEmployee ? ( { + onClick={() => { setWarnEmployee(false); }} /> @@ -69,13 +73,12 @@ const AppFrame = () => { }; function init() { - // $FlowFixMe: this element exists! ReactDOM.render(, document.getElementById('root')); initLauncherHooks(config(), store); const sessionId = store.getState().application.sessionId; initCrashReporter(sessionId || ''); - requestIdleCallback(() => { + window.requestIdleCallback(() => { setupPrefetcher(); }); } diff --git a/src/reducers/index.tsx b/src/reducers/index.tsx index 204a7a11e..1cd8f33f6 100644 --- a/src/reducers/index.tsx +++ b/src/reducers/index.tsx @@ -33,7 +33,7 @@ import storage from 'redux-persist/lib/storage/index.js'; import {Store as ReduxStore, MiddlewareAPI as ReduxMiddlewareAPI} from 'redux'; -type Actions = +export type Actions = | ApplicationAction | DevicesAction | PluginStatesAction diff --git a/src/utils/__tests__/exportData.electron.js b/src/utils/__tests__/exportData.electron.js index 840efd857..cd83bc8b5 100644 --- a/src/utils/__tests__/exportData.electron.js +++ b/src/utils/__tests__/exportData.electron.js @@ -173,7 +173,6 @@ test('test processStore function for an iOS device connected', async () => { selectedPlugins: [], }); expect(json).toBeDefined(); - // $FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {device, clients} = json; expect(device).toBeDefined(); expect(clients).toEqual([]); @@ -206,7 +205,6 @@ test('test processStore function for an iOS device connected with client plugin selectedPlugins: [], }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {pluginStates} = json.store; const expectedPluginState = { [generateClientIdentifierWithSalt(clientIdentifier, 'salt')]: { @@ -265,7 +263,6 @@ test('test processStore function to have only the client for the selected device }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already added const {clients} = json; const {pluginStates} = json.store; const expectedPluginState = { @@ -321,7 +318,6 @@ test('test processStore function to have multiple clients for the selected devic selectedPlugins: [], }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already added const {clients} = json; const {pluginStates} = json.store; const expectedPluginState = { @@ -364,7 +360,6 @@ test('test processStore function for device plugin state and no clients', async selectedPlugins: [], }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {pluginStates} = json.store; const {clients} = json; const expectedPluginState = { @@ -397,7 +392,6 @@ test('test processStore function for unselected device plugin state and no clien selectedPlugins: [], }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {pluginStates} = json.store; const {clients} = json; expect(pluginStates).toEqual({}); @@ -436,7 +430,6 @@ test('test processStore function for notifications for selected device', async ( }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {pluginStates} = json.store; const {clients} = json; expect(pluginStates).toEqual({}); @@ -493,7 +486,6 @@ test('test processStore function for notifications for unselected device', async selectedPlugins: [], }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {pluginStates} = json.store; const {clients} = json; expect(pluginStates).toEqual({}); @@ -530,7 +522,6 @@ test('test processStore function for selected plugins', async () => { selectedPlugins: ['plugin2'], }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {pluginStates} = json.store; const {clients} = json; expect(pluginStates).toEqual({ @@ -574,7 +565,6 @@ test('test processStore function for no selected plugins', async () => { }); expect(json).toBeDefined(); - //$FlowFixMe Flow doesn't that its a test and the assertion for null is already done const {pluginStates} = json.store; const {clients} = json; expect(pluginStates).toEqual({ diff --git a/types/globals.tsx b/types/globals.tsx index fc77ae6e0..3d55127e7 100644 --- a/types/globals.tsx +++ b/types/globals.tsx @@ -5,11 +5,19 @@ * @format */ +import {StoreEnhancerStoreCreator} from 'redux'; + export {}; type RequestIdleHandle = number; declare global { interface Window { + __REDUX_DEVTOOLS_EXTENSION__: undefined | StoreEnhancerStoreCreator; + + Flipper: { + init: () => void; + }; + // rIC not supportedin TS: https://github.com/Microsoft/TypeScript/issues/21309 requestIdleCallback: ( callback: (deadline: { diff --git a/yarn.lock b/yarn.lock index ad55cb619..6cf342b76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1176,6 +1176,13 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/redux-devtools-extension@^2.13.2": + version "2.13.2" + resolved "https://registry.yarnpkg.com/@types/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#b2e09a8c163b2b0f5072e6a5ac41474000df2111" + integrity sha1-suCajBY7Kw9QcualrEFHQADfIRE= + dependencies: + redux-devtools-extension "*" + "@types/redux-persist@^4.3.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@types/redux-persist/-/redux-persist-4.3.1.tgz#aa4c876859e0bea5155e5f7980e5b8c4699dc2e6" @@ -7269,6 +7276,11 @@ redux-devtools-core@^0.2.1: nanoid "^2.0.0" remotedev-serialize "^0.1.8" +redux-devtools-extension@*: + version "2.13.8" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" + integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== + redux-devtools-instrument@^1.9.4: version "1.9.6" resolved "https://registry.yarnpkg.com/redux-devtools-instrument/-/redux-devtools-instrument-1.9.6.tgz#6b412595f74b9d48cfd4ecc13e585b1588ed6e7e"