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,
|
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> {
|
|||||||
Importing data...
|
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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -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,
|
application: persistReducer(
|
||||||
|
{
|
||||||
|
key: 'application',
|
||||||
|
storage,
|
||||||
|
whitelist: ['flipperRating'],
|
||||||
|
},
|
||||||
|
application,
|
||||||
|
),
|
||||||
connections: persistReducer(
|
connections: persistReducer(
|
||||||
{
|
{
|
||||||
key: 'connections',
|
key: 'connections',
|
||||||
|
|||||||
Reference in New Issue
Block a user