Create Support Requests from Flipper

Summary: This diff adds an initial support to prefill the app information and the revision information. This diff also copies the workplace url in the clipboard.

Reviewed By: passy

Differential Revision: D16990925

fbshipit-source-id: 4f354e52de5fea07c2ea36336761d6963c27ef66
This commit is contained in:
Pritesh Nandgaonkar
2019-09-20 11:37:21 -07:00
committed by Facebook Github Bot
parent b041da6d61
commit 84c5067210
7 changed files with 192 additions and 8 deletions

View File

@@ -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<Client>;
selectedDevice: null | BaseDevice;
};
type Props = StateFromProps & OwnProps;
@@ -114,7 +117,7 @@ export class App extends React.Component<Props> {
<FlexRow grow={true}>
{this.props.leftSidebarVisible && <MainSidebar />}
{this.props.staticView != null ? (
React.createElement(this.props.staticView)
React.createElement(this.props.staticView, this.props)
) : (
<PluginContainer logger={this.props.logger} />
)}
@@ -128,12 +131,14 @@ export class App extends React.Component<Props> {
export default connect<StateFromProps, {}, OwnProps, Store>(
({
application: {leftSidebarVisible, activeSheet, share},
connections: {error, staticView},
connections: {error, staticView, clients, selectedDevice},
}) => ({
leftSidebarVisible,
activeSheet,
share: share,
error,
staticView,
clients,
selectedDevice,
}),
)(App);

View File

@@ -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<Client>;
@@ -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<Props> {
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<Props> {
</PluginName>
</ListItem>
)}
{showLithoForm && (
<ListItem
active={staticView != null && staticView === SupportRequestForm}
onClick={() => setStaticView(SupportRequestForm)}>
<PluginIcon
color={colors.light50}
name={'bell'}
isActive={
staticView != null && staticView === SupportRequestForm
}
/>
<PluginName
isActive={
staticView != null && staticView === SupportRequestForm
}>
Litho Support Request
</PluginName>
</ListItem>
)}
{selectedDevice && (
<SidebarHeader>{selectedDevice.title}</SidebarHeader>
)}
@@ -416,6 +446,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
selectedApp,
clients,
uninitializedClients,
staticView,
},
notifications: {activeNotifications, blacklistedPlugins},
plugins: {devicePlugins, clientPlugins},
@@ -428,6 +459,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
})(),
windowIsFocused,
selectedDevice,
staticView,
selectedPlugin,
selectedApp,
clients,
@@ -437,6 +469,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
}),
{
selectPlugin,
setStaticView,
setActiveSheet,
showMoreOrLessPlugins,
},

View File

@@ -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<void, void> {
render() {
return <Text> Implement your own Bug creator </Text>;
}
}

View File

@@ -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<BaseDevice>;
@@ -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,

38
src/utils/clientUtils.tsx Normal file
View File

@@ -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<Client>,
selectedDevice: null | BaseDevice,
): Array<string> {
const currentActiveApps: Array<string> = 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;
}

View File

@@ -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<any> {
// @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<Array<string>> {
// 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'))",
);
}

View File

@@ -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);
});
};