Add user ratings

Summary:
Adds a star rating to flipper internal.

Based on the nuclide behaviour:
We get an event emitted when a user changes their rating.
And we also get the rating with every ping event, which is fired every minute, if flipper is the primary window.

The only thing I don't like that much is that this doesn't actually say anywhere what the stars are for.

Reviewed By: passy

Differential Revision: D16420281

fbshipit-source-id: 69a52f64058955d7cd068215478e95c554cb9ed4
This commit is contained in:
John Knox
2019-07-24 00:11:26 -07:00
committed by Facebook Github Bot
parent a646c4e2ff
commit f7875002dd
6 changed files with 123 additions and 1 deletions

View File

@@ -0,0 +1,77 @@
/**
* 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 {Component, type Element} from 'react';
import {Glyph} from 'flipper';
import {getInstance as getLogger} from '../fb-stubs/Logger';
import GK from '../fb-stubs/GK';
type Props = {
rating: ?number,
onRatingChanged: number => void,
};
type State = {
hoveredRating: ?number,
};
export default class RatingButton extends Component<Props, State> {
state = {
hoveredRating: null,
};
onRatingChanged(rating: number) {
const previousRating = this.props.rating;
if (rating === previousRating) {
return;
}
this.props.onRatingChanged(rating);
getLogger().track('usage', 'flipper-rating-changed', {
rating,
previousRating,
});
}
render() {
if (!GK.get('flipper_rating')) {
return null;
}
const rating = this.props.rating || 0;
if (rating < 0 || rating > 5) {
throw new Error(`Rating must be between 0 and 5. Value: ${rating}`);
}
const stars = Array(5)
.fill(true)
.map<Element<*>>((_, index) => (
<div
role="button"
tabIndex={0}
onMouseEnter={() => {
this.setState({hoveredRating: index + 1});
}}
onMouseLeave={() => {
this.setState({hoveredRating: null});
}}
onClick={() => {
this.onRatingChanged(index + 1);
}}>
<Glyph
name="star"
color="grey"
variant={
(this.state.hoveredRating
? index < this.state.hoveredRating
: index < rating)
? 'filled'
: 'outline'
}
/>
</div>
));
return stars;
}
}

View File

@@ -23,7 +23,9 @@ import {
toggleLeftSidebarVisible, toggleLeftSidebarVisible,
toggleRightSidebarVisible, toggleRightSidebarVisible,
ACTIVE_SHEET_BUG_REPORTER, ACTIVE_SHEET_BUG_REPORTER,
setFlipperRating,
} from '../reducers/application.js'; } from '../reducers/application.js';
import RatingButton from './RatingButton.js';
import DevicesButton from './DevicesButton.js'; import DevicesButton from './DevicesButton.js';
import ScreenCaptureButtons from './ScreenCaptureButtons.js'; import ScreenCaptureButtons from './ScreenCaptureButtons.js';
import AutoUpdateVersion from './AutoUpdateVersion.js'; import AutoUpdateVersion from './AutoUpdateVersion.js';
@@ -62,6 +64,7 @@ type DispatchFromProps = {
toggleLeftSidebarVisible: (visible?: boolean) => void, toggleLeftSidebarVisible: (visible?: boolean) => void,
toggleRightSidebarVisible: (visible?: boolean) => void, toggleRightSidebarVisible: (visible?: boolean) => void,
setActiveSheet: (sheet: ActiveSheet) => void, setActiveSheet: (sheet: ActiveSheet) => void,
setFlipperRating: number => void,
}; };
type StateFromProps = { type StateFromProps = {
@@ -71,6 +74,7 @@ type StateFromProps = {
rightSidebarAvailable: boolean, rightSidebarAvailable: boolean,
downloadingImportData: boolean, downloadingImportData: boolean,
launcherMsg: LauncherMsg, launcherMsg: LauncherMsg,
flipperRating: ?number,
}; };
const VersionText = styled(Text)({ const VersionText = styled(Text)({
@@ -124,7 +128,14 @@ class TitleBar extends React.Component<Props> {
&nbsp;Importing data... &nbsp;Importing data...
</Importing> </Importing>
)} )}
<Spacer /> <Spacer />
{config.showFlipperRating ? (
<RatingButton
rating={this.props.flipperRating}
onRatingChanged={this.props.setFlipperRating}
/>
) : null}
<Version>{this.props.version + (isProduction() ? '' : '-dev')}</Version> <Version>{this.props.version + (isProduction() ? '' : '-dev')}</Version>
{isAutoUpdaterEnabled() ? ( {isAutoUpdaterEnabled() ? (
@@ -175,6 +186,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
rightSidebarAvailable, rightSidebarAvailable,
downloadingImportData, downloadingImportData,
launcherMsg, launcherMsg,
flipperRating,
}, },
}) => ({ }) => ({
windowIsFocused, windowIsFocused,
@@ -183,10 +195,12 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
rightSidebarAvailable, rightSidebarAvailable,
downloadingImportData, downloadingImportData,
launcherMsg, launcherMsg,
flipperRating,
}), }),
{ {
setActiveSheet, setActiveSheet,
toggleLeftSidebarVisible, toggleLeftSidebarVisible,
toggleRightSidebarVisible, toggleRightSidebarVisible,
setFlipperRating,
}, },
)(TitleBar); )(TitleBar);

View File

@@ -47,6 +47,8 @@ export default (store: Store, logger: Logger) => {
clients, clients,
} = store.getState().connections; } = store.getState().connections;
const {flipperRating} = store.getState().application;
if (!selectedDevice || !selectedPlugin) { if (!selectedDevice || !selectedPlugin) {
return; return;
} }
@@ -70,6 +72,7 @@ export default (store: Store, logger: Logger) => {
plugin: selectedPlugin, plugin: selectedPlugin,
app, app,
sdkVersion, sdkVersion,
flipperRating,
}; };
// reset dropped frames counter // reset dropped frames counter

View File

@@ -9,4 +9,5 @@ export default {
updateServer: 'https://www.facebook.com/fbflipper/public/latest.json', updateServer: 'https://www.facebook.com/fbflipper/public/latest.json',
bugReportButtonVisible: false, bugReportButtonVisible: false,
showLogin: false, showLogin: false,
showFlipperRating: false,
}; };

View File

@@ -46,6 +46,7 @@ export type State = {
serverPorts: ServerPorts, serverPorts: ServerPorts,
downloadingImportData: boolean, downloadingImportData: boolean,
launcherMsg: LauncherMsg, launcherMsg: LauncherMsg,
flipperRating: ?number,
}; };
type BooleanActionType = type BooleanActionType =
@@ -81,6 +82,12 @@ export type Action =
severity: 'warning' | 'error', severity: 'warning' | 'error',
message: string, message: string,
}, },
}
| {
type: 'SET_FLIPPER_RATING',
payload: {
rating: number,
},
}; };
const initialState: () => State = () => ({ const initialState: () => State = () => ({
@@ -100,6 +107,7 @@ const initialState: () => State = () => ({
severity: 'warning', severity: 'warning',
message: '', message: '',
}, },
flipperRating: null,
}); });
export default function reducer(state: State, action: Action): State { export default function reducer(state: State, action: Action): State {
@@ -146,6 +154,11 @@ export default function reducer(state: State, action: Action): State {
...state, ...state,
launcherMsg: action.payload, launcherMsg: action.payload,
}; };
} else if (action.type === 'SET_FLIPPER_RATING') {
return {
...state,
flipperRating: action.payload.rating,
};
} else { } else {
return state; return state;
} }
@@ -183,3 +196,10 @@ export const toggleRightSidebarAvailable = (payload?: boolean): Action => ({
type: 'rightSidebarAvailable', type: 'rightSidebarAvailable',
payload, payload,
}); });
export const setFlipperRating = (rating: number): Action => ({
type: 'SET_FLIPPER_RATING',
payload: {
rating,
},
});

View File

@@ -64,7 +64,14 @@ export type Store = ReduxStore<State, Actions>;
export type MiddlewareAPI = ReduxMiddlewareAPI<State, Actions>; export type MiddlewareAPI = ReduxMiddlewareAPI<State, Actions>;
export default combineReducers<_, Actions>({ export default combineReducers<_, Actions>({
application: persistReducer(
{
key: 'application',
storage,
whitelist: ['flipperRating'],
},
application, application,
),
connections: persistReducer( connections: persistReducer(
{ {
key: 'connections', key: 'connections',