diff --git a/src/App.tsx b/src/App.tsx index e40085510..cc7637d5a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import {FlexColumn, FlexRow} from 'flipper'; +import {FlexColumn, FlexRow, Client} from 'flipper'; import {connect} from 'react-redux'; import TitleBar from './chrome/TitleBar'; import MainSidebar from './chrome/MainSidebar'; @@ -35,6 +35,7 @@ import BugReporter from './fb-stubs/BugReporter'; import {State as Store} from './reducers/index'; import {StaticView} from './reducers/connections'; import PluginManager from './chrome/PluginManager'; +import BaseDevice from './devices/BaseDevice'; const version = remote.app.getVersion(); type OwnProps = { @@ -48,6 +49,8 @@ type StateFromProps = { activeSheet: ActiveSheet; share: ShareType | null; staticView: StaticView; + clients: Array; + selectedDevice: null | BaseDevice; }; type Props = StateFromProps & OwnProps; @@ -114,7 +117,7 @@ export class App extends React.Component { {this.props.leftSidebarVisible && } {this.props.staticView != null ? ( - React.createElement(this.props.staticView) + React.createElement(this.props.staticView, this.props) ) : ( )} @@ -128,12 +131,14 @@ export class App extends React.Component { export default connect( ({ application: {leftSidebarVisible, activeSheet, share}, - connections: {error, staticView}, + connections: {error, staticView, clients, selectedDevice}, }) => ({ leftSidebarVisible, activeSheet, share: share, error, staticView, + clients, + selectedDevice, }), )(App); diff --git a/src/chrome/MainSidebar.tsx b/src/chrome/MainSidebar.tsx index a590e0b75..11715c7b8 100644 --- a/src/chrome/MainSidebar.tsx +++ b/src/chrome/MainSidebar.tsx @@ -13,7 +13,6 @@ import {FlipperBasePlugin} from '../plugin'; import {PluginNotification} from '../reducers/notifications'; import {ActiveSheet, ACTIVE_SHEET_PLUGINS} from '../reducers/application'; import {State as Store} from '../reducers'; - import { Sidebar, FlexBox, @@ -30,7 +29,12 @@ import { } from 'flipper'; import React, {Component, PureComponent} from 'react'; import NotificationsHub from '../NotificationsHub'; -import {selectPlugin, showMoreOrLessPlugins} from '../reducers/connections'; +import { + selectPlugin, + showMoreOrLessPlugins, + StaticView, + setStaticView, +} from '../reducers/connections'; import {setActiveSheet} from '../reducers/application'; import UserAccount from './UserAccount'; import {connect} from 'react-redux'; @@ -40,6 +44,7 @@ import { SHOW_REMAINING_PLUGIN_IF_LESS_THAN, } from '../Client'; import {StyledOtherComponent} from 'create-emotion-styled'; +import SupportRequestForm from '../fb-stubs/SupportRequestForm'; const ListItem = styled('div')(({active}: {active?: boolean}) => ({ paddingLeft: 10, @@ -213,6 +218,7 @@ type StateFromProps = { numNotifications: number; windowIsFocused: boolean; selectedDevice: BaseDevice | null | undefined; + staticView: StaticView; selectedPlugin: string | null | undefined; selectedApp: string | null | undefined; clients: Array; @@ -231,9 +237,8 @@ type DispatchFromProps = { selectedApp: string | null; deepLinkPayload: string | null; }) => void; - setActiveSheet: (activeSheet: ActiveSheet) => void; - + setStaticView: (payload: StaticView) => void; showMoreOrLessPlugins: (payload: string) => void; }; @@ -243,12 +248,18 @@ class MainSidebar extends PureComponent { const { selectedDevice, selectedPlugin, + staticView, selectedApp, selectPlugin, + setStaticView, windowIsFocused, numNotifications, } = this.props; let {clients, uninitializedClients} = this.props; + const showLithoForm = + GK.get('flipper_support_requests') && + selectedDevice && + selectedDevice.os === 'Android'; clients = clients .filter( (client: Client) => @@ -295,6 +306,25 @@ class MainSidebar extends PureComponent { )} + {showLithoForm && ( + setStaticView(SupportRequestForm)}> + + + Litho Support Request + + + )} {selectedDevice && ( {selectedDevice.title} )} @@ -416,6 +446,7 @@ export default connect( selectedApp, clients, uninitializedClients, + staticView, }, notifications: {activeNotifications, blacklistedPlugins}, plugins: {devicePlugins, clientPlugins}, @@ -428,6 +459,7 @@ export default connect( })(), windowIsFocused, selectedDevice, + staticView, selectedPlugin, selectedApp, clients, @@ -437,6 +469,7 @@ export default connect( }), { selectPlugin, + setStaticView, setActiveSheet, showMoreOrLessPlugins, }, diff --git a/src/fb-stubs/SupportRequestForm.tsx b/src/fb-stubs/SupportRequestForm.tsx new file mode 100644 index 000000000..93dc8d6b9 --- /dev/null +++ b/src/fb-stubs/SupportRequestForm.tsx @@ -0,0 +1,15 @@ +/** + * 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 + */ + +import Text from '../ui/components/FlexRow'; +import React, {PureComponent} from 'react'; + +export default class SupportRequestForm extends PureComponent { + render() { + return Implement your own Bug creator ; + } +} diff --git a/src/reducers/connections.tsx b/src/reducers/connections.tsx index 32c909775..1e69bcc25 100644 --- a/src/reducers/connections.tsx +++ b/src/reducers/connections.tsx @@ -18,7 +18,12 @@ import {Actions} from '.'; const WelcomeScreen = isHeadless() ? require('../chrome/WelcomeScreenHeadless').default : require('../chrome/WelcomeScreen').default; -export type StaticView = null | typeof WelcomeScreen; +import SupportRequestForm from '../fb-stubs/SupportRequestForm'; + +export type StaticView = + | null + | typeof WelcomeScreen + | typeof SupportRequestForm; export type State = { devices: Array; @@ -135,9 +140,11 @@ const reducer = (state: State = INITAL_STATE, action: Actions): State => { switch (action.type) { case 'SET_STATIC_VIEW': { const {payload} = action; + const {selectedPlugin} = state; return { ...state, staticView: payload, + selectedPlugin: payload != null ? null : selectedPlugin, }; } case 'SELECT_DEVICE': { @@ -256,6 +263,7 @@ const reducer = (state: State = INITAL_STATE, action: Actions): State => { return { ...state, ...payload, + staticView: null, userPreferredApp: userPreferredApp, userPreferredPlugin: selectedPlugin, userLRUPlugins: selectedAppName @@ -452,6 +460,11 @@ export const selectDevice = (payload: BaseDevice): Action => ({ payload, }); +export const setStaticView = (payload: StaticView): Action => ({ + type: 'SET_STATIC_VIEW', + payload, +}); + export const preferDevice = (payload: string): Action => ({ type: 'PREFER_DEVICE', payload, diff --git a/src/utils/clientUtils.tsx b/src/utils/clientUtils.tsx new file mode 100644 index 000000000..e713650d1 --- /dev/null +++ b/src/utils/clientUtils.tsx @@ -0,0 +1,38 @@ +/** + * 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 + */ +import Client from '../Client'; +import BaseDevice from '../devices/BaseDevice'; + +export function currentActiveApps( + clients: Array, + selectedDevice: null | BaseDevice, +): Array { + const currentActiveApps: Array = clients + .map(({id}: {id: string}) => { + const appName = appNameFromClienID(id) || ''; + const device = deviceFromClienID(id) || ''; + return {appName, device}; + }) + .filter( + ({device}: {device: string}) => + device && selectedDevice && device == selectedDevice.os, + ) + .map(client => client.appName); + return currentActiveApps; +} + +export function appNameFromClienID(id: string): string | undefined { + const arr = id.split('#'); + const appName = arr[0]; + return appName; +} + +export function deviceFromClienID(id: string): string | undefined { + const arr = id.split('#'); + const device = arr[1]; + return device; +} diff --git a/src/utils/supportRequestsFormUtils.tsx b/src/utils/supportRequestsFormUtils.tsx new file mode 100644 index 000000000..bd8a0bd06 --- /dev/null +++ b/src/utils/supportRequestsFormUtils.tsx @@ -0,0 +1,62 @@ +/** + * 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 + */ +import {WebviewTag} from 'electron'; + +// TODO: Get rid off this function +function injectJavaScript(webview: WebviewTag, command: string): Promise { + // @ts-ignore: Typescript doesn't have type src in the currentTarget variable in the event, due to which there is a discrepancy in the event callback. + return webview.executeJavaScript(command, false); +} + +export function sendDidMountMessage(webview: WebviewTag) { + webview.send('hostMessage', { + type: 'onMountFlipper', + payload: null, + }); +} +/** + * + * @param webview + * @param text + * This helper function is for appending a text in the questions input text field. + * One should use it only for the pages backed by NTUsersFormContainer.react.js + */ +export function appendTextInQuestionsField(webview: WebviewTag, text: string) { + webview.send('hostMessage', { + type: 'appendQuestionString', + payload: text, + }); +} + +/** + * + * @param webview + * @param data + * This helper function is for updating a react state in NTUsersFormContainer.react.js + */ +export function updateStateInSupportForm( + webview: WebviewTag, + data: {[key: string]: any}, +) { + webview.send('hostMessage', { + type: 'updateState', + payload: data, + }); +} + +/** + * + * @param webview + * This helper function returns the supported apps by NTUsersFormContainer.react.js + */ +export function supportedApps(webview: WebviewTag): Promise> { + // TODO: Replace this with a promisified call to the guest page + return injectJavaScript( + webview, + "Array.from(document.querySelector('ul[role=radiogroup]').children).map(e => e.getAttribute('data-value'))", + ); +} diff --git a/static/SupportRequestPreload.js b/static/SupportRequestPreload.js new file mode 100644 index 000000000..5fe14e33b --- /dev/null +++ b/static/SupportRequestPreload.js @@ -0,0 +1,18 @@ +/** + * 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 {ipcRenderer} = require('electron'); + +global.sendToHost = message => { + ipcRenderer.sendToHost(message); +}; + +global.setupToReceiveHostMessage = callback => { + ipcRenderer.on('hostMessage', (event, message) => { + callback(message); + }); +};