Tune in command palette (#389)

This commit is contained in:
Piotr Rogowski 2022-01-23 21:09:17 +01:00 committed by GitHub
parent 800db9a139
commit c458c24c91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 138 additions and 69 deletions

View File

@ -55,6 +55,7 @@ html, body {
font-size: @font-size-sm;
color: @text-color;
z-index: @bars-z-index;
padding-top: 5px;
// border-top-width: 1px;
// border-top-color: @border-color-split;
// border-top-style: solid;

View File

@ -6,6 +6,7 @@ import {
useMemo,
ReactNode,
useCallback,
useEffect,
} from 'react';
import {
ActionId,
@ -17,7 +18,10 @@ import {
KBarResults,
useMatches,
ActionImpl,
Action,
useKBar,
} from 'kbar';
import { connect } from 'react-redux';
import {
CloudUploadOutlined,
LoginOutlined,
@ -25,6 +29,11 @@ import {
LogoutOutlined,
} from '@ant-design/icons';
import { useHistory } from 'react-router';
import {
Config as ConfigType,
Tune as TuneType,
Menus as MenusType,
} from '@speedy-tuner/types';
import { Routes } from '../routes';
import { useAuth } from '../contexts/AuthContext';
import {
@ -33,6 +42,16 @@ import {
} from '../pages/auth/notifications';
import store from '../store';
import { isMac } from '../utils/env';
import {
AppState,
NavigationState,
} from '../types/state';
import {
buildUrl,
SKIP_MENUS,
SKIP_SUB_MENUS,
} from './Tune/SideBar';
import Icon from './SideBar/Icon';
enum Sections {
NAVIGATION = 'Navigation',
@ -183,12 +202,71 @@ const RenderResults = () => {
);
};
const CommandPalette = (props: { children: ReactNode }) => {
const { children } = props;
const history = useHistory();
const { logout } = useAuth();
const mapStateToProps = (state: AppState) => ({
config: state.config,
tune: state.tune,
ui: state.ui,
navigation: state.navigation,
});
const logoutAction = useCallback(async() => {
interface CommandPaletteProps {
config: ConfigType;
tune: TuneType;
navigation: NavigationState;
children?: ReactNode;
};
const ActionsProvider = (props: CommandPaletteProps) => {
const { config, tune, navigation } = props;
const { query } = useKBar();
const history = useHistory();
const generateActions = useCallback((types: MenusType) => {
const newActions: Action[] = [];
Object.keys(types).forEach((menuName: string) => {
if (SKIP_MENUS.includes(menuName)) {
return;
}
Object.keys(types[menuName].subMenus).forEach((subMenuName: string) => {
if (subMenuName === 'std_separator') {
return;
}
if (SKIP_SUB_MENUS.includes(`${menuName}/${subMenuName}`)) {
return;
}
const subMenu = types[menuName].subMenus[subMenuName];
newActions.push({
id: buildUrl(navigation.tuneId!, menuName, subMenuName),
section: types[menuName].title,
name: subMenu.title,
icon: <Icon name={subMenuName} />,
perform: () => history.push(buildUrl(navigation.tuneId!, menuName, subMenuName)),
});
});
});
return newActions;
}, [history, navigation.tuneId]);
useEffect(() => {
if (Object.keys(tune.constants).length) {
query.registerActions(generateActions(config.menus));
}
}, [config.menus, generateActions, query, tune.constants]);
return null;
};
const CommandPalette = (props: CommandPaletteProps) => {
const { children, config, tune, navigation } = props;
const { logout } = useAuth();
const history = useHistory();
const logoutAction = useCallback(async () => {
try {
await logout();
logOutSuccessful();
@ -198,7 +276,7 @@ const CommandPalette = (props: { children: ReactNode }) => {
}
}, [logout]);
const initialActions = useMemo(() => [
const initialActions = [
{
id: 'ToggleSidebar',
name: 'Toggle Sidebar',
@ -207,37 +285,37 @@ const CommandPalette = (props: { children: ReactNode }) => {
},
{
id: 'UploadAction',
name: 'Upload',
section: Sections.NAVIGATION,
icon: <CloudUploadOutlined />,
name: 'Upload',
subtitle: 'Upload tune and logs.',
icon: <CloudUploadOutlined />,
perform: () => history.push(Routes.UPLOAD),
},
{
id: 'LoginAction',
name: 'Login',
section: Sections.AUTH,
icon: <LoginOutlined />,
name: 'Login',
subtitle: 'Login using email, Google or GitHub account.',
icon: <LoginOutlined />,
perform: () => history.push(Routes.LOGIN),
},
{
id: 'SignUpAction',
name: 'Sign-up',
section: Sections.AUTH,
icon: <UserAddOutlined />,
name: 'Sign-up',
subtitle: 'Create new account.',
icon: <UserAddOutlined />,
perform: () => history.push(Routes.SIGN_UP),
},
{
id: 'LogoutAction',
name: 'Logout',
section: Sections.AUTH,
icon: <LogoutOutlined />,
name: 'Logout',
subtitle: 'Logout current user.',
icon: <LogoutOutlined />,
perform: logoutAction,
},
], [history, logoutAction]);
];
return (
<KBarProvider actions={initialActions}>
@ -249,9 +327,10 @@ const CommandPalette = (props: { children: ReactNode }) => {
</KBarAnimator>
</KBarPositioner>
</KBarPortal>
<ActionsProvider config={config} tune={tune} navigation={navigation} />
{children}
</KBarProvider>
);
};
export default CommandPalette;
export default connect(mapStateToProps)(CommandPalette);

View File

@ -19,20 +19,39 @@ import {
Menus as MenusType,
Tune as TuneType,
} from '@speedy-tuner/types';
import store from '../store';
import Icon from './SideBar/Icon';
import { evaluateExpression } from '../utils/tune/expression';
import { Routes } from '../routes';
import useConfig from '../hooks/useConfig';
import store from '../../store';
import Icon from '../SideBar/Icon';
import { Routes } from '../../routes';
import useConfig from '../../hooks/useConfig';
import {
AppState,
NavigationState,
UIState,
} from '../types/state';
} from '../../types/state';
const { Sider } = Layout;
const { SubMenu } = Menu;
export const SKIP_MENUS = [
'help',
'hardwareTesting',
'3dTuningMaps',
'dataLogging',
'tools',
];
export const SKIP_SUB_MENUS = [
'settings/gaugeLimits',
'settings/io_summary',
'tuning/std_realtime',
];
export const buildUrl = (tuneId: string, main: string, sub: string) => generatePath(Routes.TUNE_DIALOG, {
tuneId,
category: main,
dialog: sub,
});
export interface DialogMatchedPathType {
url: string;
params: {
@ -48,33 +67,15 @@ const mapStateToProps = (state: AppState) => ({
navigation: state.navigation,
});
const SKIP_MENUS = [
'help',
'hardwareTesting',
'3dTuningMaps',
'dataLogging',
'tools',
];
interface SideBarProps {
config: ConfigType;
tune: TuneType;
ui: UIState;
navigation: NavigationState;
matchedPath: DialogMatchedPathType;
};
const SKIP_SUB_MENUS = [
'settings/gaugeLimits',
'settings/io_summary',
'tuning/std_realtime',
];
const SideBar = ({
config,
tune,
ui,
navigation,
matchedPath,
}: {
config: ConfigType,
tune: TuneType,
ui: UIState,
navigation: NavigationState,
matchedPath: DialogMatchedPathType,
}) => {
const SideBar = ({ config, tune, ui, navigation, matchedPath }: SideBarProps) => {
const sidebarWidth = 250;
const siderProps = {
width: sidebarWidth,
@ -84,12 +85,6 @@ const SideBar = ({
onCollapse: (collapsed: boolean) => store.dispatch({ type: 'ui/sidebarCollapsed', payload: collapsed }),
} as any;
const { isConfigReady } = useConfig(config);
const checkCondition = useCallback((condition: string) => evaluateExpression(condition, tune.constants, config), [tune.constants, config]);
const buildUrl = useCallback((main: string, sub: string) => generatePath(Routes.TUNE_DIALOG, {
tuneId: navigation.tuneId!,
category: main,
dialog: sub,
}), [navigation.tuneId]);
const [menus, setMenus] = useState<any[]>([]);
const menusList = useCallback((types: MenusType) => (
@ -103,29 +98,23 @@ const SideBar = ({
key={`/${menuName}`}
icon={<Icon name={menuName} />}
title={types[menuName].title}
onTitleClick={() => store.dispatch({ type: 'ui/sidebarCollapsed', payload: false })}
>
{Object.keys(types[menuName].subMenus).map((subMenuName: string) => {
if (subMenuName === 'std_separator') {
return <Menu.Divider key={buildUrl(menuName, subMenuName)} />;
return <Menu.Divider key={buildUrl(navigation.tuneId!, menuName, subMenuName)} />;
}
if (SKIP_SUB_MENUS.includes(`${menuName}/${subMenuName}`)) {
return null;
}
const subMenu = types[menuName].subMenus[subMenuName];
let enabled = true;
if (subMenu.condition) {
enabled = checkCondition(subMenu.condition);
}
return (<Menu.Item
key={buildUrl(menuName, subMenuName)}
key={buildUrl(navigation.tuneId!, menuName, subMenuName)}
icon={<Icon name={subMenuName} />}
disabled={!enabled}
>
<Link to={buildUrl(menuName, subMenuName)}>
<Link to={buildUrl(navigation.tuneId!, menuName, subMenuName)}>
{subMenu.title}
</Link>
</Menu.Item>);
@ -133,7 +122,7 @@ const SideBar = ({
</SubMenu>
);
})
), [buildUrl, checkCondition]);
), [navigation.tuneId]);
useEffect(() => {
if (Object.keys(tune.constants).length) {

View File

@ -9,7 +9,7 @@ import { connect } from 'react-redux';
import { useMemo } from 'react';
import { Config as ConfigType } from '@speedy-tuner/types';
import Dialog from '../components/Tune/Dialog';
import SideBar, { DialogMatchedPathType } from '../components/SideBar';
import SideBar, { DialogMatchedPathType } from '../components/Tune/SideBar';
import { Routes } from '../routes';
import useBrowserStorage from '../hooks/useBrowserStorage';
import useConfig from '../hooks/useConfig';

View File

@ -7,7 +7,7 @@
@layout-header-height: 45px;
@layout-footer-padding: 2px 10px;
@layout-footer-height: 24px;
@layout-footer-height: 28px;
@bars-z-index: 5;
@zindex-modal: 1080;

View File

@ -87,7 +87,7 @@
// Side
@layout-sider-background: @main;
@layout-trigger-background: @main;
@layout-trigger-background: @component-background;
@layout-trigger-color: @text-color;
// Tooltip

View File

@ -7,5 +7,5 @@
@layout-header-background: @main;
@layout-sider-background: @main;
@layout-trigger-background: @main;
@layout-trigger-background: @component-background;
@layout-trigger-color: @text-color;