diff --git a/desktop/flipper-ui-core/src/chrome/RatingButton.tsx b/desktop/flipper-ui-core/src/chrome/RatingButton.tsx
deleted file mode 100644
index a36d20ff6..000000000
--- a/desktop/flipper-ui-core/src/chrome/RatingButton.tsx
+++ /dev/null
@@ -1,359 +0,0 @@
-/**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @format
- */
-
-import React, {
- Component,
- ReactElement,
- useCallback,
- useEffect,
- useState,
-} from 'react';
-import {
- FlexColumn,
- FlexRow,
- Button,
- Checkbox,
- styled,
- Input,
- Link,
-} from '../ui';
-import {LeftRailButton} from '../sandy-chrome/LeftRail';
-import * as UserFeedback from '../fb-stubs/UserFeedback';
-import {FeedbackPrompt} from '../fb-stubs/UserFeedback';
-import {StarOutlined} from '@ant-design/icons';
-import {Popover, Rate} from 'antd';
-import {useStore} from '../utils/useStore';
-import {currentUser} from '../fb-stubs/user';
-import {theme, useValue} from 'flipper-plugin';
-import {reportPlatformFailures} from 'flipper-common';
-import {getRenderHostInstance} from 'flipper-frontend-core';
-
-type NextAction = 'select-rating' | 'leave-comment' | 'finished';
-
-class PredefinedComment extends Component<{
- comment: string;
- selected: boolean;
- onClick: (_: unknown) => unknown;
-}> {
- static Container = styled.div<{selected: boolean}>((props) => {
- return {
- border: '1px solid #f2f3f5',
- cursor: 'pointer',
- borderRadius: 24,
- backgroundColor: props.selected ? '#ecf3ff' : '#f2f3f5',
- marginBottom: 4,
- marginRight: 4,
- padding: '4px 8px',
- color: props.selected ? 'rgb(56, 88, 152)' : undefined,
- borderColor: props.selected ? '#3578e5' : undefined,
- ':hover': {
- borderColor: '#3578e5',
- },
- };
- });
- render() {
- return (
-
- {this.props.comment}
-
- );
- }
-}
-
-const Row = styled(FlexRow)({
- marginTop: 5,
- marginBottom: 5,
- justifyContent: 'center',
- textAlign: 'center',
- color: '#9a9a9a',
- flexWrap: 'wrap',
-});
-
-const DismissRow = styled(Row)({
- marginBottom: 0,
- marginTop: 10,
-});
-
-const DismissButton = styled.span({
- '&:hover': {
- textDecoration: 'underline',
- cursor: 'pointer',
- },
-});
-
-const Spacer = styled(FlexColumn)({
- flexGrow: 1,
-});
-
-function dismissRow(dismiss: () => void) {
- return (
-
-
- Dismiss
-
-
- );
-}
-
-type FeedbackComponentState = {
- rating: number | null;
- hoveredRating: number;
- allowUserInfoSharing: boolean;
- nextAction: NextAction;
- predefinedComments: {[key: string]: boolean};
- comment: string;
-};
-
-class FeedbackComponent extends Component<
- {
- submitRating: (rating: number) => void;
- submitComment: (
- rating: number,
- comment: string,
- selectedPredefinedComments: Array,
- allowUserInfoSharing: boolean,
- ) => void;
- close: () => void;
- dismiss: () => void;
- promptData: FeedbackPrompt;
- },
- FeedbackComponentState
-> {
- state: FeedbackComponentState = {
- rating: null,
- hoveredRating: 0,
- allowUserInfoSharing: true,
- nextAction: 'select-rating' as NextAction,
- predefinedComments: this.props.promptData.predefinedComments.reduce(
- (acc, cv) => ({...acc, [cv]: false}),
- {},
- ),
- comment: '',
- };
- onSubmitRating(newRating: number) {
- const nextAction = newRating <= 2 ? 'leave-comment' : 'finished';
- this.setState({rating: newRating, nextAction: nextAction});
- this.props.submitRating(newRating);
- if (nextAction === 'finished') {
- setTimeout(this.props.close, 5000);
- }
- }
- onCommentSubmitted(comment: string) {
- this.setState({nextAction: 'finished'});
- const selectedPredefinedComments: Array = Object.entries(
- this.state.predefinedComments,
- )
- .map((x) => ({comment: x[0], enabled: x[1]}))
- .filter((x) => x.enabled)
- .map((x) => x.comment);
- const currentRating = this.state.rating;
- if (currentRating) {
- this.props.submitComment(
- currentRating,
- comment,
- selectedPredefinedComments,
- this.state.allowUserInfoSharing,
- );
- } else {
- console.error('Illegal state: Submitting comment with no rating set.');
- }
- setTimeout(this.props.close, 1000);
- }
- onAllowUserSharingChanged(allowed: boolean) {
- this.setState({allowUserInfoSharing: allowed});
- }
- render() {
- let body: Array;
- switch (this.state.nextAction) {
- case 'select-rating':
- body = [
- {this.props.promptData.bodyText}
,
-
- this.onSubmitRating(newRating)} />
-
,
- dismissRow(this.props.dismiss),
- ];
- break;
- case 'leave-comment':
- const predefinedComments = Object.entries(
- this.state.predefinedComments,
- ).map((c: [string, unknown], idx: number) => (
-
- this.setState({
- predefinedComments: {
- ...this.state.predefinedComments,
- [c[0]]: !c[1],
- },
- })
- }
- />
- ));
- body = [
- {predefinedComments}
,
-
- this.setState({comment: e.target.value})}
- onKeyDown={(e) =>
- e.key == 'Enter' && this.onCommentSubmitted(this.state.comment)
- }
- autoFocus
- />
-
,
-
-
- {'Tool owner can contact me '}
-
,
-
-
-
,
- dismissRow(this.props.dismiss),
- ];
- break;
- case 'finished':
- body = [
-
- Thanks for the feedback! You can now help
-
- prioritize bugs and features for Flipper in Papercuts
-
-
,
- dismissRow(this.props.dismiss),
- ];
- break;
- default: {
- console.error('Illegal state: nextAction: ' + this.state.nextAction);
- return null;
- }
- }
- return (
-
-
- {this.state.nextAction === 'finished'
- ? this.props.promptData.postSubmitHeading
- : this.props.promptData.preSubmitHeading}
-
- {body}
-
- );
- }
-}
-
-export function SandyRatingButton() {
- const [promptData, setPromptData] =
- useState(null);
- const [isShown, setIsShown] = useState(false);
- const [hasTriggered, setHasTriggered] = useState(false);
- const sessionId = useStore((store) => store.application.sessionId);
- const loggedIn = useValue(currentUser());
-
- const triggerPopover = useCallback(() => {
- if (!hasTriggered) {
- setIsShown(true);
- setHasTriggered(true);
- }
- }, [hasTriggered]);
-
- useEffect(() => {
- if (
- getRenderHostInstance().GK('flipper_enable_star_ratiings') &&
- !hasTriggered &&
- loggedIn
- ) {
- reportPlatformFailures(
- UserFeedback.getPrompt().then((prompt) => {
- setPromptData(prompt);
- setTimeout(triggerPopover, 30000);
- }),
- 'RatingButton:getPrompt',
- ).catch((e) => {
- console.warn('Failed to load ratings prompt:', e);
- });
- }
- }, [triggerPopover, hasTriggered, loggedIn]);
-
- const onClick = () => {
- const willBeShown = !isShown;
- setIsShown(willBeShown);
- setHasTriggered(true);
- if (!willBeShown) {
- UserFeedback.dismiss(sessionId);
- }
- };
-
- const submitRating = (rating: number) => {
- UserFeedback.submitRating(rating, sessionId);
- };
-
- const submitComment = (
- rating: number,
- comment: string,
- selectedPredefinedComments: Array,
- allowUserInfoSharing: boolean,
- ) => {
- UserFeedback.submitComment(
- rating,
- comment,
- selectedPredefinedComments,
- allowUserInfoSharing,
- sessionId,
- );
- };
-
- if (!promptData) {
- return null;
- }
- if (!promptData.shouldPopup || (hasTriggered && !isShown)) {
- return null;
- }
- return (
- {
- setIsShown(false);
- }}
- dismiss={onClick}
- promptData={promptData}
- />
- }
- placement="right"
- trigger="click">
- }
- title="Rate Flipper"
- onClick={onClick}
- small
- />
-
- );
-}
diff --git a/desktop/flipper-ui-core/src/sandy-chrome/LeftRail.tsx b/desktop/flipper-ui-core/src/sandy-chrome/LeftRail.tsx
deleted file mode 100644
index 91faf40c7..000000000
--- a/desktop/flipper-ui-core/src/sandy-chrome/LeftRail.tsx
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @format
- */
-
-import React, {cloneElement, useState, useCallback} from 'react';
-import {Button, Divider, Badge, Tooltip, Menu} from 'antd';
-import {SettingOutlined} from '@ant-design/icons';
-import {useStore} from '../utils/useStore';
-import {
- theme,
- withTrackingScope,
- useTrackedCallback,
- NUX,
-} from 'flipper-plugin';
-import SettingsSheet from '../chrome/SettingsSheet';
-import WelcomeScreen from './WelcomeScreen';
-import styled from '@emotion/styled';
-import {setStaticView} from '../reducers/connections';
-import constants from '../fb-stubs/constants';
-import {
- canFileExport,
- canOpenDialog,
- showOpenDialog,
- startFileExport,
- startLinkExport,
-} from '../utils/exportData';
-import {openDeeplinkDialog} from '../deeplink';
-import {css} from '@emotion/css';
-import {getRenderHostInstance} from 'flipper-frontend-core';
-import {StyleGuide} from './StyleGuide';
-
-const LeftRailButtonElem = styled(Button)<{kind?: 'small'}>(({kind}) => ({
- width: kind === 'small' ? 32 : 36,
- height: kind === 'small' ? 32 : 36,
- padding: '5px 0',
- border: 'none',
- boxShadow: 'none',
-}));
-LeftRailButtonElem.displayName = 'LeftRailButtonElem';
-
-export function LeftRailButton({
- icon,
- small,
- selected,
- toggled,
- count,
- title,
- onClick,
- disabled,
-}: {
- icon?: React.ReactElement;
- small?: boolean;
- toggled?: boolean;
- selected?: boolean;
- disabled?: boolean;
- count?: number | true;
- title?: string;
- onClick?: React.MouseEventHandler;
-}) {
- const iconElement =
- icon && cloneElement(icon, {style: {fontSize: small ? 16 : 24}});
-
- let res = (
-
- );
-
- if (count !== undefined) {
- res =
- count === true ? (
-
- {res}
-
- ) : (
-
- {res}
-
- );
- }
-
- if (title) {
- res = (
-
- {res}
-
- );
- }
-
- return res;
-}
-
-const LeftRailDivider = styled(Divider)({
- margin: `10px 0`,
- width: 32,
- minWidth: 32,
-});
-LeftRailDivider.displayName = 'LeftRailDividier';
-
-export const LeftRail = withTrackingScope(function LeftRail() {
- return ;
-});
-
-const menu = css`
- border: none;
-`;
-const submenu = css`
- .ant-menu-submenu-title {
- width: 32px;
- height: 32px !important;
- line-height: 32px !important;
- padding: 0;
- margin: 0;
- }
- .ant-menu-submenu-arrow {
- display: none;
- }
-`;
-
-function ExtrasMenu() {
- const store = useStore();
-
- const startFileExportTracked = useTrackedCallback(
- 'File export',
- () => startFileExport(store.dispatch),
- [store.dispatch],
- );
- const startLinkExportTracked = useTrackedCallback(
- 'Link export',
- () => startLinkExport(store.dispatch),
- [store.dispatch],
- );
- const startImportTracked = useTrackedCallback(
- 'File import',
- () => showOpenDialog(store),
- [store],
- );
-
- const [showSettings, setShowSettings] = useState(false);
- const onSettingsClose = useCallback(() => setShowSettings(false), []);
-
- const settings = useStore((state) => state.settingsState);
- const {showWelcomeAtStartup} = settings;
- const [welcomeVisible, setWelcomeVisible] = useState(showWelcomeAtStartup);
-
- return (
- <>
-
-
-
- {showSettings && (
-
- )}
- setWelcomeVisible(false)}
- showAtStartup={showWelcomeAtStartup}
- onCheck={(value) =>
- store.dispatch({
- type: 'UPDATE_SETTINGS',
- payload: {...settings, showWelcomeAtStartup: value},
- })
- }
- />
- >
- );
-}
diff --git a/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx b/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx
index af23b3adb..0192c2063 100644
--- a/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx
+++ b/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx
@@ -30,21 +30,20 @@ import {useDispatch, useStore} from '../utils/useStore';
import config from '../fb-stubs/config';
import {isConnected, currentUser, logoutUser} from '../fb-stubs/user';
import {showLoginDialog} from '../chrome/fb-stubs/SignInSheet';
-import {Avatar, Badge, Button, Modal, Popover, Tooltip} from 'antd';
+import {Avatar, Badge, Button, Menu, Modal, Popover, Tooltip} from 'antd';
import {
ApiOutlined,
AppstoreAddOutlined,
BellOutlined,
BugOutlined,
CameraOutlined,
- EllipsisOutlined,
FileExclamationOutlined,
LayoutOutlined,
LoginOutlined,
MedicineBoxOutlined,
MobileOutlined,
- QuestionCircleOutlined,
RocketOutlined,
+ SettingOutlined,
VideoCameraOutlined,
WarningOutlined,
} from '@ant-design/icons';
@@ -62,10 +61,22 @@ import NetworkGraph from '../chrome/NetworkGraph';
import {errorCounterAtom} from '../chrome/ConsoleLogs';
import {filterNotifications} from './notification/notificationUtils';
import {
+ canFileExport,
+ canOpenDialog,
exportEverythingEverywhereAllAtOnce,
ExportEverythingEverywhereAllAtOnceStatus,
+ showOpenDialog,
+ startFileExport,
+ startLinkExport,
} from '../utils/exportData';
+import UpdateIndicator from '../chrome/UpdateIndicator';
import {css} from '@emotion/css';
+import constants from '../fb-stubs/constants';
+import {setStaticView} from '../reducers/connections';
+import {StyleGuide} from './StyleGuide';
+import {openDeeplinkDialog} from '../deeplink';
+import SettingsSheet from '../chrome/SettingsSheet';
+import WelcomeScreen from './WelcomeScreen';
export const Navbar = withTrackingScope(function Navbar({
toplevelSelection,
@@ -126,12 +137,12 @@ export const Navbar = withTrackingScope(function Navbar({
toplevelSelection={toplevelSelection}
setToplevelSelection={setToplevelSelection}
/>
+
-
-
{config.showLogin && }
+
);
@@ -607,3 +618,131 @@ export function LeftRailButton({
return res;
}
+
+const menu = css`
+ border: none;
+ height: 56px;
+
+ .ant-menu-submenu-title {
+ hieght: 56px;
+ }
+`;
+const submenu = css`
+ height: 56px;
+
+ .ant-menu-submenu-title {
+ width: 61px !important;
+ height: 56px !important;
+ padding: 0;
+ margin: 0;
+ }
+ .ant-menu-submenu-arrow {
+ display: none;
+ }
+`;
+
+function ExtrasMenu() {
+ const store = useStore();
+
+ const startFileExportTracked = useTrackedCallback(
+ 'File export',
+ () => startFileExport(store.dispatch),
+ [store.dispatch],
+ );
+ const startLinkExportTracked = useTrackedCallback(
+ 'Link export',
+ () => startLinkExport(store.dispatch),
+ [store.dispatch],
+ );
+ const startImportTracked = useTrackedCallback(
+ 'File import',
+ () => showOpenDialog(store),
+ [store],
+ );
+
+ const [showSettings, setShowSettings] = useState(false);
+ const onSettingsClose = useCallback(() => setShowSettings(false), []);
+
+ const settings = useStore((state) => state.settingsState);
+ const {showWelcomeAtStartup} = settings;
+ const [welcomeVisible, setWelcomeVisible] = useState(showWelcomeAtStartup);
+
+ return (
+ <>
+
+
+
+ {showSettings && (
+
+ )}
+ setWelcomeVisible(false)}
+ showAtStartup={showWelcomeAtStartup}
+ onCheck={(value) =>
+ store.dispatch({
+ type: 'UPDATE_SETTINGS',
+ payload: {...settings, showWelcomeAtStartup: value},
+ })
+ }
+ />
+ >
+ );
+}
diff --git a/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx b/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx
index f370a2e97..1d8a11589 100644
--- a/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx
+++ b/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx
@@ -21,7 +21,6 @@ import {theme} from 'flipper-plugin';
import {Logger} from 'flipper-common';
import {Navbar} from './Navbar';
-import {LeftRail} from './LeftRail';
import {useStore, useDispatch} from '../utils/useStore';
import {FlipperDevTools} from '../chrome/FlipperDevTools';
import {setStaticView} from '../reducers/connections';
@@ -173,9 +172,12 @@ export function SandyApp() {
toplevelSelection={toplevelSelection}
setToplevelSelection={setToplevelSelection}
/>
-
+
-
<_Sidebar width={250} minWidth={220} maxWidth={800} gutter>
{leftMenuContent && (