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:
committed by
Facebook Github Bot
parent
a646c4e2ff
commit
f7875002dd
77
src/chrome/RatingButton.js
Normal file
77
src/chrome/RatingButton.js
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,4 +9,5 @@ export default {
|
||||
updateServer: 'https://www.facebook.com/fbflipper/public/latest.json',
|
||||
bugReportButtonVisible: false,
|
||||
showLogin: false,
|
||||
showFlipperRating: false,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user