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

View File

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

View File

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

View File

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