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:
committed by
Facebook Github Bot
parent
b041da6d61
commit
84c5067210
11
src/App.tsx
11
src/App.tsx
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {FlexColumn, FlexRow} from 'flipper';
|
import {FlexColumn, FlexRow, Client} from 'flipper';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import TitleBar from './chrome/TitleBar';
|
import TitleBar from './chrome/TitleBar';
|
||||||
import MainSidebar from './chrome/MainSidebar';
|
import MainSidebar from './chrome/MainSidebar';
|
||||||
@@ -35,6 +35,7 @@ import BugReporter from './fb-stubs/BugReporter';
|
|||||||
import {State as Store} from './reducers/index';
|
import {State as Store} from './reducers/index';
|
||||||
import {StaticView} from './reducers/connections';
|
import {StaticView} from './reducers/connections';
|
||||||
import PluginManager from './chrome/PluginManager';
|
import PluginManager from './chrome/PluginManager';
|
||||||
|
import BaseDevice from './devices/BaseDevice';
|
||||||
const version = remote.app.getVersion();
|
const version = remote.app.getVersion();
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@@ -48,6 +49,8 @@ type StateFromProps = {
|
|||||||
activeSheet: ActiveSheet;
|
activeSheet: ActiveSheet;
|
||||||
share: ShareType | null;
|
share: ShareType | null;
|
||||||
staticView: StaticView;
|
staticView: StaticView;
|
||||||
|
clients: Array<Client>;
|
||||||
|
selectedDevice: null | BaseDevice;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = StateFromProps & OwnProps;
|
type Props = StateFromProps & OwnProps;
|
||||||
@@ -114,7 +117,7 @@ export class App extends React.Component<Props> {
|
|||||||
<FlexRow grow={true}>
|
<FlexRow grow={true}>
|
||||||
{this.props.leftSidebarVisible && <MainSidebar />}
|
{this.props.leftSidebarVisible && <MainSidebar />}
|
||||||
{this.props.staticView != null ? (
|
{this.props.staticView != null ? (
|
||||||
React.createElement(this.props.staticView)
|
React.createElement(this.props.staticView, this.props)
|
||||||
) : (
|
) : (
|
||||||
<PluginContainer logger={this.props.logger} />
|
<PluginContainer logger={this.props.logger} />
|
||||||
)}
|
)}
|
||||||
@@ -128,12 +131,14 @@ export class App extends React.Component<Props> {
|
|||||||
export default connect<StateFromProps, {}, OwnProps, Store>(
|
export default connect<StateFromProps, {}, OwnProps, Store>(
|
||||||
({
|
({
|
||||||
application: {leftSidebarVisible, activeSheet, share},
|
application: {leftSidebarVisible, activeSheet, share},
|
||||||
connections: {error, staticView},
|
connections: {error, staticView, clients, selectedDevice},
|
||||||
}) => ({
|
}) => ({
|
||||||
leftSidebarVisible,
|
leftSidebarVisible,
|
||||||
activeSheet,
|
activeSheet,
|
||||||
share: share,
|
share: share,
|
||||||
error,
|
error,
|
||||||
staticView,
|
staticView,
|
||||||
|
clients,
|
||||||
|
selectedDevice,
|
||||||
}),
|
}),
|
||||||
)(App);
|
)(App);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {FlipperBasePlugin} from '../plugin';
|
|||||||
import {PluginNotification} from '../reducers/notifications';
|
import {PluginNotification} from '../reducers/notifications';
|
||||||
import {ActiveSheet, ACTIVE_SHEET_PLUGINS} from '../reducers/application';
|
import {ActiveSheet, ACTIVE_SHEET_PLUGINS} from '../reducers/application';
|
||||||
import {State as Store} from '../reducers';
|
import {State as Store} from '../reducers';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
FlexBox,
|
FlexBox,
|
||||||
@@ -30,7 +29,12 @@ import {
|
|||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
import React, {Component, PureComponent} from 'react';
|
import React, {Component, PureComponent} from 'react';
|
||||||
import NotificationsHub from '../NotificationsHub';
|
import NotificationsHub from '../NotificationsHub';
|
||||||
import {selectPlugin, showMoreOrLessPlugins} from '../reducers/connections';
|
import {
|
||||||
|
selectPlugin,
|
||||||
|
showMoreOrLessPlugins,
|
||||||
|
StaticView,
|
||||||
|
setStaticView,
|
||||||
|
} from '../reducers/connections';
|
||||||
import {setActiveSheet} from '../reducers/application';
|
import {setActiveSheet} from '../reducers/application';
|
||||||
import UserAccount from './UserAccount';
|
import UserAccount from './UserAccount';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
@@ -40,6 +44,7 @@ import {
|
|||||||
SHOW_REMAINING_PLUGIN_IF_LESS_THAN,
|
SHOW_REMAINING_PLUGIN_IF_LESS_THAN,
|
||||||
} from '../Client';
|
} from '../Client';
|
||||||
import {StyledOtherComponent} from 'create-emotion-styled';
|
import {StyledOtherComponent} from 'create-emotion-styled';
|
||||||
|
import SupportRequestForm from '../fb-stubs/SupportRequestForm';
|
||||||
|
|
||||||
const ListItem = styled('div')(({active}: {active?: boolean}) => ({
|
const ListItem = styled('div')(({active}: {active?: boolean}) => ({
|
||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
@@ -213,6 +218,7 @@ type StateFromProps = {
|
|||||||
numNotifications: number;
|
numNotifications: number;
|
||||||
windowIsFocused: boolean;
|
windowIsFocused: boolean;
|
||||||
selectedDevice: BaseDevice | null | undefined;
|
selectedDevice: BaseDevice | null | undefined;
|
||||||
|
staticView: StaticView;
|
||||||
selectedPlugin: string | null | undefined;
|
selectedPlugin: string | null | undefined;
|
||||||
selectedApp: string | null | undefined;
|
selectedApp: string | null | undefined;
|
||||||
clients: Array<Client>;
|
clients: Array<Client>;
|
||||||
@@ -231,9 +237,8 @@ type DispatchFromProps = {
|
|||||||
selectedApp: string | null;
|
selectedApp: string | null;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: string | null;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
|
||||||
setActiveSheet: (activeSheet: ActiveSheet) => void;
|
setActiveSheet: (activeSheet: ActiveSheet) => void;
|
||||||
|
setStaticView: (payload: StaticView) => void;
|
||||||
showMoreOrLessPlugins: (payload: string) => void;
|
showMoreOrLessPlugins: (payload: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -243,12 +248,18 @@ class MainSidebar extends PureComponent<Props> {
|
|||||||
const {
|
const {
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
selectedPlugin,
|
selectedPlugin,
|
||||||
|
staticView,
|
||||||
selectedApp,
|
selectedApp,
|
||||||
selectPlugin,
|
selectPlugin,
|
||||||
|
setStaticView,
|
||||||
windowIsFocused,
|
windowIsFocused,
|
||||||
numNotifications,
|
numNotifications,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let {clients, uninitializedClients} = this.props;
|
let {clients, uninitializedClients} = this.props;
|
||||||
|
const showLithoForm =
|
||||||
|
GK.get('flipper_support_requests') &&
|
||||||
|
selectedDevice &&
|
||||||
|
selectedDevice.os === 'Android';
|
||||||
clients = clients
|
clients = clients
|
||||||
.filter(
|
.filter(
|
||||||
(client: Client) =>
|
(client: Client) =>
|
||||||
@@ -295,6 +306,25 @@ class MainSidebar extends PureComponent<Props> {
|
|||||||
</PluginName>
|
</PluginName>
|
||||||
</ListItem>
|
</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 && (
|
{selectedDevice && (
|
||||||
<SidebarHeader>{selectedDevice.title}</SidebarHeader>
|
<SidebarHeader>{selectedDevice.title}</SidebarHeader>
|
||||||
)}
|
)}
|
||||||
@@ -416,6 +446,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
|||||||
selectedApp,
|
selectedApp,
|
||||||
clients,
|
clients,
|
||||||
uninitializedClients,
|
uninitializedClients,
|
||||||
|
staticView,
|
||||||
},
|
},
|
||||||
notifications: {activeNotifications, blacklistedPlugins},
|
notifications: {activeNotifications, blacklistedPlugins},
|
||||||
plugins: {devicePlugins, clientPlugins},
|
plugins: {devicePlugins, clientPlugins},
|
||||||
@@ -428,6 +459,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
|||||||
})(),
|
})(),
|
||||||
windowIsFocused,
|
windowIsFocused,
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
|
staticView,
|
||||||
selectedPlugin,
|
selectedPlugin,
|
||||||
selectedApp,
|
selectedApp,
|
||||||
clients,
|
clients,
|
||||||
@@ -437,6 +469,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
selectPlugin,
|
selectPlugin,
|
||||||
|
setStaticView,
|
||||||
setActiveSheet,
|
setActiveSheet,
|
||||||
showMoreOrLessPlugins,
|
showMoreOrLessPlugins,
|
||||||
},
|
},
|
||||||
|
|||||||
15
src/fb-stubs/SupportRequestForm.tsx
Normal file
15
src/fb-stubs/SupportRequestForm.tsx
Normal 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>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,12 @@ import {Actions} from '.';
|
|||||||
const WelcomeScreen = isHeadless()
|
const WelcomeScreen = isHeadless()
|
||||||
? require('../chrome/WelcomeScreenHeadless').default
|
? require('../chrome/WelcomeScreenHeadless').default
|
||||||
: require('../chrome/WelcomeScreen').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 = {
|
export type State = {
|
||||||
devices: Array<BaseDevice>;
|
devices: Array<BaseDevice>;
|
||||||
@@ -135,9 +140,11 @@ const reducer = (state: State = INITAL_STATE, action: Actions): State => {
|
|||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'SET_STATIC_VIEW': {
|
case 'SET_STATIC_VIEW': {
|
||||||
const {payload} = action;
|
const {payload} = action;
|
||||||
|
const {selectedPlugin} = state;
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
staticView: payload,
|
staticView: payload,
|
||||||
|
selectedPlugin: payload != null ? null : selectedPlugin,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'SELECT_DEVICE': {
|
case 'SELECT_DEVICE': {
|
||||||
@@ -256,6 +263,7 @@ const reducer = (state: State = INITAL_STATE, action: Actions): State => {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...payload,
|
...payload,
|
||||||
|
staticView: null,
|
||||||
userPreferredApp: userPreferredApp,
|
userPreferredApp: userPreferredApp,
|
||||||
userPreferredPlugin: selectedPlugin,
|
userPreferredPlugin: selectedPlugin,
|
||||||
userLRUPlugins: selectedAppName
|
userLRUPlugins: selectedAppName
|
||||||
@@ -452,6 +460,11 @@ export const selectDevice = (payload: BaseDevice): Action => ({
|
|||||||
payload,
|
payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setStaticView = (payload: StaticView): Action => ({
|
||||||
|
type: 'SET_STATIC_VIEW',
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
|
||||||
export const preferDevice = (payload: string): Action => ({
|
export const preferDevice = (payload: string): Action => ({
|
||||||
type: 'PREFER_DEVICE',
|
type: 'PREFER_DEVICE',
|
||||||
payload,
|
payload,
|
||||||
|
|||||||
38
src/utils/clientUtils.tsx
Normal file
38
src/utils/clientUtils.tsx
Normal 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;
|
||||||
|
}
|
||||||
62
src/utils/supportRequestsFormUtils.tsx
Normal file
62
src/utils/supportRequestsFormUtils.tsx
Normal 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'))",
|
||||||
|
);
|
||||||
|
}
|
||||||
18
static/SupportRequestPreload.js
Normal file
18
static/SupportRequestPreload.js
Normal 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user