fbshipit-source-id: c71048dfea2a03cf83650b55aa9d1e463251920c
This commit is contained in:
@@ -58,4 +58,4 @@ matrix:
|
||||
|
||||
script:
|
||||
- cd iOS/Sample
|
||||
- xcodebuild clean build -workspace Sample.xcworkspace -scheme Pods-Sample
|
||||
- xcodebuild clean build -workspace Sample.xcworkspace -scheme Pods-Sample -sdk iphonesimulator11.2
|
||||
|
||||
@@ -63,23 +63,17 @@ public class MyApplication extends Application {
|
||||
To integrate with an iOS app, you can use [CocoaPods](https://cocoapods.org). Add the mobile Sonar SDK and its dependencies to your `Podfile`:
|
||||
|
||||
```ruby
|
||||
platform :ios, '8.0'
|
||||
swift_version = '4.1'
|
||||
project 'MyApp.xcodeproj'
|
||||
source 'https://github.com/facebook/Sonar.git'
|
||||
source 'https://github.com/CocoaPods/Specs'
|
||||
# Uncomment the next line to define a global platform for your project
|
||||
swift_version = "4.1"
|
||||
|
||||
target 'MyApp' do
|
||||
|
||||
pod 'RSocket', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/third-party-podspecs/RSocket.podspec'
|
||||
pod 'DoubleConversion', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/third-party-podspecs/DoubleConversion.podspec'
|
||||
pod 'glog', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/third-party-podspecs/glog.podspec'
|
||||
pod 'Folly', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/third-party-podspecs/Folly.podspec'
|
||||
pod 'PeerTalk', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/third-party-podspecs/PeerTalk.podspec'
|
||||
pod 'Yoga','~>1.8.1', :modular_headers => true
|
||||
pod 'Sonar', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/xplat/Sonar/Sonar.podspec'
|
||||
pod 'SonarKit', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/SonarKit.podspec'
|
||||
pod 'SonarKit/SonarKitLayoutComponentKitSupport', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/SonarKit.podspec'
|
||||
pod 'SonarKit/SKIOSNetworkPlugin', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/SonarKit.podspec'
|
||||
pod 'ComponentKit', :podspec => 'https://raw.githubusercontent.com/facebook/Sonar/master/iOS/third-party-podspecs/ComponentKit.podspec'
|
||||
pod 'SonarKit', '~>0.0.1'
|
||||
post_install do |installer|
|
||||
|
||||
installer.pods_project.targets.each do |target|
|
||||
if ['YogaKit'].include? target.name
|
||||
target.build_configurations.each do |config|
|
||||
@@ -112,7 +106,7 @@ and install the dependencies by running `pod install`. When you open the Xcode w
|
||||
```
|
||||
<div class='warning'>
|
||||
|
||||
* We haven't released the dependency to CocoaPods, because we weren't able to successfully validate the podspec of SonarKit. You could help us out by fixing this [issue](https://github.com/facebook/Sonar/issues/11) by submitting a PR to the repo.
|
||||
* We haven't released the dependency to CocoaPods yet, here is the [issue](https://github.com/facebook/Sonar/issues/132) by which you can track.
|
||||
* If you do not use CocoaPods as a dependency management tool then currently there is no way to integrate SonarKit other than manually including all the dependencies and building it.
|
||||
* For Android, Sonar works with both emulators and physical devices connected through USB. However on iOS, we don't yet support physical devices.
|
||||
* Also Sonar doesn't work with swift projects as its written in C++ and had C++ dependencies. But we are working on supporting sonar for swift projects. You can find this issue [here](https://github.com/facebook/Sonar/issues/13)
|
||||
|
||||
214
src/App.js
214
src/App.js
@@ -4,185 +4,41 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {FlexColumn, FlexRow} from 'sonar';
|
||||
import {connect} from 'react-redux';
|
||||
import {toggleBugDialogVisible} from './reducers/application.js';
|
||||
import WelcomeScreen from './chrome/WelcomeScreen.js';
|
||||
import SonarTitleBar from './chrome/SonarTitleBar.js';
|
||||
import BaseDevice from './devices/BaseDevice.js';
|
||||
import MainSidebar from './chrome/MainSidebar.js';
|
||||
import {SonarBasePlugin} from './plugin.js';
|
||||
import Server from './server.js';
|
||||
import Client from './Client.js';
|
||||
import React from 'react';
|
||||
import BugReporter from './fb-stubs/BugReporter.js';
|
||||
import BugReporterDialog from './chrome/BugReporterDialog.js';
|
||||
import ErrorBar from './chrome/ErrorBar.js';
|
||||
import Logger from './fb-stubs/Logger.js';
|
||||
import PluginContainer from './PluginContainer.js';
|
||||
import PluginManager from './chrome/PluginManager.js';
|
||||
const electron = require('electron');
|
||||
const yargs = require('yargs');
|
||||
|
||||
export type {Client};
|
||||
|
||||
export type StatePluginInfo = {
|
||||
plugin: ?SonarBasePlugin<>,
|
||||
state: Object,
|
||||
};
|
||||
|
||||
export type StateClientPlugins = {
|
||||
[pluginKey: string]: StatePluginInfo,
|
||||
};
|
||||
|
||||
export type StatePlugins = {
|
||||
[appKey: string]: StateClientPlugins,
|
||||
};
|
||||
|
||||
export type State = {
|
||||
activePluginKey: ?string,
|
||||
activeAppKey: ?string,
|
||||
plugins: StatePlugins,
|
||||
error: ?string,
|
||||
};
|
||||
import type Logger from './fb-stubs/Logger.js';
|
||||
import type BugReporter from './fb-stubs/BugReporter.js';
|
||||
|
||||
type Props = {
|
||||
devices: Array<BaseDevice>,
|
||||
logger: Logger,
|
||||
bugReporter: BugReporter,
|
||||
leftSidebarVisible: boolean,
|
||||
bugDialogVisible: boolean,
|
||||
pluginManagerVisible: boolean,
|
||||
selectedDeviceIndex: number,
|
||||
selectedApp: ?string,
|
||||
error: ?string,
|
||||
toggleBugDialogVisible: (visible?: boolean) => void,
|
||||
};
|
||||
|
||||
export class App extends React.Component<Props, State> {
|
||||
constructor() {
|
||||
export class App extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
performance.mark('init');
|
||||
super();
|
||||
this.initTracking();
|
||||
|
||||
setupEnvironment();
|
||||
this.logger = new Logger();
|
||||
replaceGlobalConsole(this.logger);
|
||||
this.server = this.initServer();
|
||||
|
||||
this.state = {
|
||||
activeAppKey: null,
|
||||
activePluginKey: null,
|
||||
error: null,
|
||||
devices: {},
|
||||
plugins: {},
|
||||
};
|
||||
|
||||
this.bugReporter = new BugReporter(this.logger);
|
||||
this.commandLineArgs = yargs.parse(electron.remote.process.argv);
|
||||
super(props);
|
||||
}
|
||||
|
||||
server: Server;
|
||||
bugReporter: BugReporter;
|
||||
logger: Logger;
|
||||
commandLineArgs: Object;
|
||||
_hasActivatedPreferredPlugin: boolean = false;
|
||||
|
||||
componentDidMount() {
|
||||
this.logger.trackTimeSince('init');
|
||||
|
||||
// close socket before reloading
|
||||
window.addEventListener('beforeunload', () => {
|
||||
this.server.close();
|
||||
});
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return null;
|
||||
}
|
||||
|
||||
initServer(): Server {
|
||||
const server = new Server(this);
|
||||
server.addListener('new-client', client => {
|
||||
client.addListener('close', () => {
|
||||
this.setState(state => {
|
||||
this.forceUpdate();
|
||||
// TODO:
|
||||
//reducers.TeardownClient(this, state, {appKey: client.id}),
|
||||
});
|
||||
if (this.state.activeAppKey === client.id) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
client.addListener('plugins-change', () => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
});
|
||||
|
||||
server.addListener('clients-change', () => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
|
||||
server.addListener('error', err => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
this.setState({
|
||||
error:
|
||||
"Couldn't start websocket server. " +
|
||||
'Looks like you have multiple copies of Sonar running.',
|
||||
});
|
||||
} else {
|
||||
// unknown error
|
||||
this.setState({
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
initTracking = () => {
|
||||
electron.ipcRenderer.on('trackUsage', () => {
|
||||
// check if there's a plugin currently active
|
||||
const {activeAppKey, activePluginKey} = this.state;
|
||||
if (activeAppKey == null || activePluginKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// app plugins
|
||||
const client = this.getClient(activeAppKey);
|
||||
if (client) {
|
||||
this.logger.track('usage', 'ping', {
|
||||
app: client.query.app,
|
||||
device: client.query.device,
|
||||
os: client.query.os,
|
||||
plugin: activePluginKey,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// device plugins
|
||||
const device: ?BaseDevice = this.getDevice(activeAppKey);
|
||||
if (device) {
|
||||
this.logger.track('usage', 'ping', {
|
||||
os: device.os,
|
||||
plugin: activePluginKey,
|
||||
device: device.title,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getDevice = (id: string): ?BaseDevice =>
|
||||
this.props.devices.find((device: BaseDevice) => device.serial === id);
|
||||
|
||||
getClient(appKey: ?string): ?Client {
|
||||
if (appKey == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const info = this.server.connections.get(appKey);
|
||||
if (info != null) {
|
||||
return info.client;
|
||||
}
|
||||
this.props.logger.trackTimeSince('init');
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -191,30 +47,21 @@ export class App extends React.Component<Props, State> {
|
||||
<SonarTitleBar />
|
||||
{this.props.bugDialogVisible && (
|
||||
<BugReporterDialog
|
||||
bugReporter={this.bugReporter}
|
||||
bugReporter={this.props.bugReporter}
|
||||
close={() => this.props.toggleBugDialogVisible(false)}
|
||||
/>
|
||||
)}
|
||||
{this.props.selectedDeviceIndex > -1 ? (
|
||||
<FlexRow fill={true}>
|
||||
{this.props.leftSidebarVisible && (
|
||||
<MainSidebar
|
||||
clients={Array.from(this.server.connections.values()).map(
|
||||
({client}) => client,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<PluginContainer
|
||||
logger={this.logger}
|
||||
client={this.getClient(this.props.selectedApp)}
|
||||
/>
|
||||
{this.props.leftSidebarVisible && <MainSidebar />}
|
||||
<PluginContainer logger={this.props.logger} />
|
||||
</FlexRow>
|
||||
) : this.props.pluginManagerVisible ? (
|
||||
<PluginManager />
|
||||
) : (
|
||||
<WelcomeScreen />
|
||||
)}
|
||||
<ErrorBar text={this.state.error} />
|
||||
<ErrorBar text={this.props.error} />
|
||||
</FlexColumn>
|
||||
);
|
||||
}
|
||||
@@ -223,39 +70,14 @@ export class App extends React.Component<Props, State> {
|
||||
export default connect(
|
||||
({
|
||||
application: {pluginManagerVisible, bugDialogVisible, leftSidebarVisible},
|
||||
connections: {devices, selectedDeviceIndex, selectedApp},
|
||||
connections: {selectedDeviceIndex},
|
||||
server: {error},
|
||||
}) => ({
|
||||
pluginManagerVisible,
|
||||
bugDialogVisible,
|
||||
leftSidebarVisible,
|
||||
devices,
|
||||
selectedDeviceIndex,
|
||||
selectedApp,
|
||||
error,
|
||||
}),
|
||||
{toggleBugDialogVisible},
|
||||
)(App);
|
||||
|
||||
function replaceGlobalConsole(logger: Logger) {
|
||||
const loggerMethods = {
|
||||
log: logger.info,
|
||||
warn: logger.warn,
|
||||
error: logger.error,
|
||||
};
|
||||
const consoleHandler = {
|
||||
get: function(obj, prop) {
|
||||
return prop in loggerMethods
|
||||
? args => {
|
||||
obj[prop] && obj[prop](args);
|
||||
return loggerMethods[prop].bind(logger)(args);
|
||||
}
|
||||
: obj[prop];
|
||||
},
|
||||
};
|
||||
window.console = new Proxy(console, consoleHandler);
|
||||
}
|
||||
|
||||
function setupEnvironment() {
|
||||
if (!process.env.ANDROID_HOME) {
|
||||
process.env.ANDROID_HOME = '/opt/android_sdk';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
import type {SonarPlugin} from './plugin.js';
|
||||
import type {App} from './App.js';
|
||||
import type BaseDevice from './devices/BaseDevice.js';
|
||||
import type Logger from './fb-stubs/Logger.js';
|
||||
|
||||
import plugins from './plugins/index.js';
|
||||
import {ReactiveSocket, PartialResponder} from 'rsocket-core';
|
||||
|
||||
@@ -26,7 +27,12 @@ export type ClientQuery = {|
|
||||
type RequestMetadata = {method: string, id: number, params: ?Object};
|
||||
|
||||
export default class Client extends EventEmitter {
|
||||
constructor(app: App, id: string, query: ClientQuery, conn: ReactiveSocket) {
|
||||
constructor(
|
||||
id: string,
|
||||
query: ClientQuery,
|
||||
conn: ReactiveSocket,
|
||||
logger: Logger,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.connected = true;
|
||||
@@ -35,7 +41,7 @@ export default class Client extends EventEmitter {
|
||||
this.id = id;
|
||||
this.query = query;
|
||||
this.messageIdCounter = 0;
|
||||
this.app = app;
|
||||
this.logger = logger;
|
||||
|
||||
this.broadcastCallbacks = new Map();
|
||||
this.requestCallbacks = new Map();
|
||||
@@ -82,16 +88,6 @@ export default class Client extends EventEmitter {
|
||||
|},
|
||||
>;
|
||||
|
||||
getDevice(): ?BaseDevice {
|
||||
const {device_id} = this.query;
|
||||
|
||||
if (device_id == null) {
|
||||
return null;
|
||||
} else {
|
||||
return this.app.getDevice(device_id);
|
||||
}
|
||||
}
|
||||
|
||||
supportsPlugin(Plugin: Class<SonarPlugin<>>): boolean {
|
||||
return this.plugins.includes(Plugin.id);
|
||||
}
|
||||
@@ -193,7 +189,7 @@ export default class Client extends EventEmitter {
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return null;
|
||||
return `<Client#${this.id}>`;
|
||||
}
|
||||
|
||||
subscribe(
|
||||
@@ -257,7 +253,7 @@ export default class Client extends EventEmitter {
|
||||
finishTimingRequestResponse(data: RequestMetadata) {
|
||||
const mark = this.getPerformanceMark(data);
|
||||
const logEventName = this.getLogEventName(data);
|
||||
this.app.logger.trackTimeSince(mark, logEventName);
|
||||
this.logger.trackTimeSince(mark, logEventName);
|
||||
}
|
||||
|
||||
getPerformanceMark(data: RequestMetadata): string {
|
||||
|
||||
@@ -36,8 +36,9 @@ type Props = {
|
||||
logger: LogManager,
|
||||
selectedDeviceIndex: number,
|
||||
selectedPlugin: ?string,
|
||||
selectedApp: ?string,
|
||||
pluginStates: Object,
|
||||
client: ?Client,
|
||||
clients: Array<Client>,
|
||||
devices: Array<BaseDevice>,
|
||||
setPluginState: (payload: {
|
||||
pluginKey: string,
|
||||
@@ -101,13 +102,15 @@ class PluginContainer extends Component<Props, State> {
|
||||
if (activePlugin) {
|
||||
pluginKey = `${device.serial}#${activePlugin.id}`;
|
||||
} else {
|
||||
target = props.clients.find(
|
||||
(client: Client) => client.id === props.selectedApp,
|
||||
);
|
||||
activePlugin = plugins.find(
|
||||
(p: Class<SonarPlugin<>>) => p.id === props.selectedPlugin,
|
||||
);
|
||||
if (!activePlugin || !props.client) {
|
||||
if (!activePlugin || !target) {
|
||||
return null;
|
||||
}
|
||||
target = props.client;
|
||||
pluginKey = `${target.id}#${activePlugin.id}`;
|
||||
}
|
||||
|
||||
@@ -161,13 +164,16 @@ class PluginContainer extends Component<Props, State> {
|
||||
export default connect(
|
||||
({
|
||||
application: {rightSidebarVisible, rightSidebarAvailable},
|
||||
connections: {selectedPlugin, devices, selectedDeviceIndex},
|
||||
connections: {selectedPlugin, devices, selectedDeviceIndex, selectedApp},
|
||||
pluginStates,
|
||||
server: {clients},
|
||||
}) => ({
|
||||
selectedPlugin,
|
||||
devices,
|
||||
selectedDeviceIndex,
|
||||
pluginStates,
|
||||
selectedApp,
|
||||
clients,
|
||||
}),
|
||||
{
|
||||
setPluginState,
|
||||
|
||||
@@ -221,11 +221,13 @@ class MainSidebar extends Component<MainSidebarProps> {
|
||||
export default connect(
|
||||
({
|
||||
connections: {devices, selectedDeviceIndex, selectedPlugin, selectedApp},
|
||||
server: {clients},
|
||||
}) => ({
|
||||
devices,
|
||||
selectedDeviceIndex,
|
||||
selectedPlugin,
|
||||
selectedApp,
|
||||
clients,
|
||||
}),
|
||||
{
|
||||
selectPlugin,
|
||||
|
||||
@@ -554,6 +554,7 @@ export default class LogTable extends SonarDevicePlugin<LogsState> {
|
||||
defaultFilters={DEFAULT_FILTERS}
|
||||
zebra={false}
|
||||
actions={<Button onClick={this.clearLogs}>Clear Logs</Button>}
|
||||
stickyBottom={true}
|
||||
/>
|
||||
</LogTable.ContextMenu>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import AndroidDevice from '../devices/AndroidDevice';
|
||||
import child_process from 'child_process';
|
||||
import type {Store} from '../reducers/index.js';
|
||||
import type BaseDevice from '../devices/BaseDevice';
|
||||
import type Logger from '../fb-stubs/Logger.js';
|
||||
const adb = require('adbkit-fb');
|
||||
|
||||
function createDecive(client, device): Promise<AndroidDevice> {
|
||||
@@ -47,7 +48,7 @@ function getRunningEmulatorName(id: string): Promise<?string> {
|
||||
});
|
||||
}
|
||||
|
||||
export default (store: Store) => {
|
||||
export default (store: Store, logger: Logger) => {
|
||||
const client = adb.createClient();
|
||||
|
||||
// get emulators
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
|
||||
import {remote} from 'electron';
|
||||
import type {Store} from '../reducers/index.js';
|
||||
import type Logger from '../fb-stubs/Logger.js';
|
||||
|
||||
export default (store: Store) => {
|
||||
export default (store: Store, logger: Logger) => {
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.on('focus', () =>
|
||||
store.dispatch({
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
import type {ChildProcess} from 'child_process';
|
||||
import type {Store} from '../reducers/index.js';
|
||||
import type Logger from '../fb-stubs/Logger.js';
|
||||
|
||||
import child_process from 'child_process';
|
||||
import IOSDevice from '../devices/IOSDevice';
|
||||
|
||||
@@ -49,7 +51,7 @@ function querySimulatorDevices(): Promise<IOSDeviceMap> {
|
||||
});
|
||||
}
|
||||
|
||||
export default (store: Store) => {
|
||||
export default (store: Store, logger: Logger) => {
|
||||
// monitoring iOS devices only available on MacOS.
|
||||
if (process.platform !== 'darwin') {
|
||||
return;
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
import androidDevice from './androidDevice';
|
||||
import iOSDevice from './iOSDevice';
|
||||
import application from './application';
|
||||
import tracking from './tracking';
|
||||
import server from './server';
|
||||
|
||||
import type Logger from '../fb-stubs/Logger.js';
|
||||
import type {Store} from '../reducers/index.js';
|
||||
|
||||
export default (store: Store) =>
|
||||
[application, androidDevice, iOSDevice].forEach(fn => fn(store));
|
||||
export default (store: Store, logger: Logger) =>
|
||||
[application, androidDevice, iOSDevice, tracking, server].forEach(fn =>
|
||||
fn(store, logger),
|
||||
);
|
||||
|
||||
44
src/dispatcher/server.js
Normal file
44
src/dispatcher/server.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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 Server from '../server.js';
|
||||
|
||||
import type {Store} from '../reducers/index.js';
|
||||
import type Logger from '../fb-stubs/Logger.js';
|
||||
|
||||
export default (store: Store, logger: Logger) => {
|
||||
const server = new Server(logger);
|
||||
server.addListener('new-client', (client: Client) => {
|
||||
store.dispatch({
|
||||
type: 'NEW_CLIENT',
|
||||
payload: client,
|
||||
});
|
||||
});
|
||||
|
||||
server.addListener('removed-client', (id: string) => {
|
||||
store.dispatch({
|
||||
type: 'CLIENT_REMOVED',
|
||||
payload: id,
|
||||
});
|
||||
});
|
||||
|
||||
server.addListener('error', err => {
|
||||
const payload: string =
|
||||
err.code === 'EADDRINUSE'
|
||||
? "Couldn't start websocket server. Looks like you have multiple copies of Sonar running."
|
||||
: err.message || 'Unknown error';
|
||||
|
||||
store.dispatch({
|
||||
type: 'SERVER_ERROR',
|
||||
payload,
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
server.close();
|
||||
});
|
||||
};
|
||||
44
src/dispatcher/tracking.js
Normal file
44
src/dispatcher/tracking.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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 {ipcRenderer} from 'electron';
|
||||
|
||||
import type BaseDevice from '../devices/BaseDevice.js';
|
||||
import type {Store} from '../reducers/index.js';
|
||||
import type Logger from '../fb-stubs/Logger.js';
|
||||
|
||||
export default (store: Store, logger: Logger) => {
|
||||
ipcRenderer.on('trackUsage', () => {
|
||||
const {
|
||||
devices,
|
||||
selectedDeviceIndex,
|
||||
selectedPlugin,
|
||||
selectedApp,
|
||||
} = store.getState().connections;
|
||||
|
||||
const device: ?BaseDevice =
|
||||
selectedDeviceIndex > -1 ? devices[selectedDeviceIndex] : null;
|
||||
console.log(1, 2, 3);
|
||||
if (!device || !selectedPlugin) {
|
||||
return;
|
||||
}
|
||||
if (selectedApp) {
|
||||
logger.track('usage', 'ping', {
|
||||
app: selectedApp,
|
||||
device,
|
||||
os: device.os,
|
||||
plugin: selectedPlugin,
|
||||
});
|
||||
} else {
|
||||
logger.track('usage', 'ping', {
|
||||
os: device.os,
|
||||
plugin: selectedPlugin,
|
||||
device: device.title,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -10,7 +10,9 @@ import ReactDOM from 'react-dom';
|
||||
import {ContextMenuProvider} from 'sonar';
|
||||
import {precachedIcons} from './utils/icons.js';
|
||||
import GK from './fb-stubs/GK.js';
|
||||
import Logger from './fb-stubs/Logger.js';
|
||||
import App from './App.js';
|
||||
import BugReporter from './fb-stubs/BugReporter.js';
|
||||
import {createStore} from 'redux';
|
||||
import reducers from './reducers/index.js';
|
||||
import dispatcher from './dispatcher/index.js';
|
||||
@@ -22,15 +24,16 @@ const store = createStore(
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
|
||||
);
|
||||
|
||||
dispatcher(store);
|
||||
|
||||
const logger = new Logger();
|
||||
const bugReporter = new BugReporter(logger);
|
||||
dispatcher(store, logger);
|
||||
GK.init();
|
||||
setupMenuBar();
|
||||
|
||||
const AppFrame = () => (
|
||||
<ContextMenuProvider>
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
<App logger={logger} bugReporter={bugReporter} />
|
||||
</Provider>
|
||||
</ContextMenuProvider>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import {combineReducers} from 'redux';
|
||||
import application from './application.js';
|
||||
import connections from './connections.js';
|
||||
import server from './server.js';
|
||||
import pluginStates from './pluginStates.js';
|
||||
import type {
|
||||
State as ApplicationState,
|
||||
@@ -21,6 +22,7 @@ import type {
|
||||
State as PluginsState,
|
||||
Action as PluginsAction,
|
||||
} from './pluginStates.js';
|
||||
import type {State as ServerState, Action as ServerAction} from './server.js';
|
||||
import type {Store as ReduxStore} from 'redux';
|
||||
|
||||
export type Store = ReduxStore<
|
||||
@@ -28,8 +30,14 @@ export type Store = ReduxStore<
|
||||
application: ApplicationState,
|
||||
connections: DevicesState,
|
||||
pluginStates: PluginsState,
|
||||
server: ServerState,
|
||||
},
|
||||
ApplicationAction | DevicesAction | PluginsAction,
|
||||
ApplicationAction | DevicesAction | PluginsAction | ServerAction,
|
||||
>;
|
||||
|
||||
export default combineReducers({application, connections, pluginStates});
|
||||
export default combineReducers({
|
||||
application,
|
||||
connections,
|
||||
pluginStates,
|
||||
server,
|
||||
});
|
||||
|
||||
54
src/reducers/server.js
Normal file
54
src/reducers/server.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export type State = {
|
||||
error: ?string,
|
||||
clients: Array<Client>,
|
||||
};
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
type: 'SERVER_ERROR',
|
||||
payload: ?string,
|
||||
}
|
||||
| {
|
||||
type: 'NEW_CLIENT',
|
||||
payload: Client,
|
||||
}
|
||||
| {
|
||||
type: 'CLIENT_REMOVED',
|
||||
payload: string,
|
||||
};
|
||||
|
||||
const INITIAL_STATE: State = {
|
||||
error: null,
|
||||
clients: [],
|
||||
};
|
||||
|
||||
export default function reducer(
|
||||
state: State = INITIAL_STATE,
|
||||
action: Action,
|
||||
): State {
|
||||
if (action.type === 'NEW_CLIENT') {
|
||||
const {payload} = action;
|
||||
return {
|
||||
...state,
|
||||
clients: state.clients.concat(payload),
|
||||
};
|
||||
} else if (action.type === 'CLIENT_REMOVED') {
|
||||
const {payload} = action;
|
||||
return {
|
||||
...state,
|
||||
clients: state.clients.filter((client: Client) => client.id !== payload),
|
||||
};
|
||||
} else if (action.type === 'SERVER_ERROR') {
|
||||
const {payload} = action;
|
||||
return {...state, error: payload};
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {App} from './App.js';
|
||||
import type {SecureServerConfig} from './utils/CertificateProvider';
|
||||
import type Logger from './fb-stubs/Logger';
|
||||
import type {ClientQuery} from './Client.js';
|
||||
@@ -40,14 +39,14 @@ export default class Server extends EventEmitter {
|
||||
insecureServer: RSocketServer;
|
||||
certificateProvider: CertificateProvider;
|
||||
connectionTracker: ConnectionTracker;
|
||||
app: App;
|
||||
logger: Logger;
|
||||
|
||||
constructor(app: App) {
|
||||
constructor(logger: Logger) {
|
||||
super();
|
||||
this.app = app;
|
||||
this.logger = logger;
|
||||
this.connections = new Map();
|
||||
this.certificateProvider = new CertificateProvider(this, app.logger);
|
||||
this.connectionTracker = new ConnectionTracker(app.logger);
|
||||
this.certificateProvider = new CertificateProvider(this, logger);
|
||||
this.connectionTracker = new ConnectionTracker(logger);
|
||||
this.init();
|
||||
}
|
||||
|
||||
@@ -186,7 +185,7 @@ export default class Server extends EventEmitter {
|
||||
const id = `${query.app}-${query.os}-${query.device}`;
|
||||
console.warn(`Device connected: ${id}`, 'connection');
|
||||
|
||||
const client = new Client(this.app, id, query, conn);
|
||||
const client = new Client(id, query, conn, this.logger);
|
||||
|
||||
const info = {
|
||||
client,
|
||||
@@ -235,6 +234,7 @@ export default class Server extends EventEmitter {
|
||||
info.client.emit('close');
|
||||
this.connections.delete(id);
|
||||
this.emit('clients-change');
|
||||
this.emit('removed-client', id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ const fs = require('fs');
|
||||
const compilePlugins = require('./compilePlugins.js');
|
||||
const os = require('os');
|
||||
|
||||
if (!process.env.ANDROID_HOME) {
|
||||
process.env.ANDROID_HOME = '/opt/android_sdk';
|
||||
}
|
||||
|
||||
// ensure .sonar folder and config exist
|
||||
const sonarDir = path.join(os.homedir(), '.sonar');
|
||||
if (!fs.existsSync(sonarDir)) {
|
||||
|
||||
Reference in New Issue
Block a user