feat: preferences as tab (#6786)

* feat: preferences as tab

refactor: remove preferences tab from permanent tabs and update tab label handling

fix: comment

Co-authored-by: Sid <siddharth@usebruno.com>

* refactor: replace Checkbox component with native input elements in Preferences and ProxySettings

---------

Co-authored-by: Sid <siddharth@usebruno.com>
This commit is contained in:
Pragadesh-45
2026-01-21 19:22:28 +05:30
committed by GitHub
parent 2288121f70
commit 67c1d39e60
24 changed files with 232 additions and 105 deletions

View File

@@ -6,7 +6,7 @@ import { savePreferences } from 'providers/ReduxStore/slices/app';
import StyledWrapper from './StyledWrapper';
import toast from 'react-hot-toast';
const Font = ({ close }) => {
const Font = () => {
const dispatch = useDispatch();
const preferences = useSelector((state) => state.app.preferences);
const isInitialMount = useRef(true);

View File

@@ -2,6 +2,22 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
color: ${(props) => props.theme.text};
.text-link {
color: ${(props) => props.theme.colors.text.link};
text-decoration: none;
font-size: 0.8125rem;
&:hover {
text-decoration: underline;
}
}
form.bruno-form {
label {
font-size: 0.8125rem;
}
}
`;
export default StyledWrapper;

View File

@@ -11,7 +11,7 @@ import toast from 'react-hot-toast';
import path from 'utils/common/path';
import { IconTrash } from '@tabler/icons';
const General = ({ close }) => {
const General = () => {
const preferences = useSelector((state) => state.app.preferences);
const dispatch = useDispatch();
const inputFileCaCertificateRef = useRef();

View File

@@ -2,12 +2,12 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
table {
width: 100%;
width: 80%;
border-collapse: collapse;
thead,
td {
border: 2px solid ${(props) => props.theme.table.border};
border: 1px solid ${(props) => props.theme.table.border};
}
thead {
@@ -17,7 +17,7 @@ const StyledWrapper = styled.div`
}
td {
padding: 4px 8px;
padding: 6px 10px;
font-size: ${(props) => props.theme.font.size.sm};
}
@@ -25,6 +25,7 @@ const StyledWrapper = styled.div`
font-weight: 500;
padding: 10px;
text-align: left;
border: 1px solid ${(props) => props.theme.table.border};
}
}
@@ -35,11 +36,13 @@ const StyledWrapper = styled.div`
.key-button {
display: inline-block;
color: ${(props) => props.theme.table.input.color};
opacity: 0.7;
border-radius: 4px;
padding: 1px 5px;
font-family: monospace;
margin-right: 8px;
border: 1px solid #ccc;
border-bottom: 1.44px solid ${(props) => props.theme.table.input.border};
}
`;

View File

@@ -291,12 +291,14 @@ const ProxySettings = ({ close }) => {
Auth
</label>
<input
id="config.auth.disabled"
type="checkbox"
name="config.auth.disabled"
checked={!formik.values.config.auth.disabled}
onChange={(e) => {
formik.setFieldValue('config.auth.disabled', !e.target.checked);
}}
className="mousetrap mr-0"
/>
</div>
<div>

View File

@@ -2,7 +2,7 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
div.tabs {
padding: 8px;
padding: 12px;
min-width: 160px;
div.tab {
@@ -28,10 +28,10 @@ const StyledWrapper = styled.div`
&.active {
color: ${(props) => props.theme.text} !important;
background: ${(props) => props.theme.modal.title.bg};
background: ${(props) => props.theme.tabs.secondary.active.bg};
&:hover {
background: ${(props) => props.theme.modal.title.bg} !important;
background: ${(props) => props.theme.tabs.secondary.active.bg} !important;
}
}
}
@@ -39,9 +39,9 @@ const StyledWrapper = styled.div`
section.tab-panel {
min-height: 70vh;
max-height: 70vh;
overflow-y: auto;
width: clamp(300px, 45vw, 550px);
flex-grow: 1;
padding: 12px;
}
input[type="checkbox"],
@@ -50,6 +50,24 @@ const StyledWrapper = styled.div`
cursor: pointer;
}
.textbox {
line-height: 1.5;
padding: 0.45rem;
border-radius: ${(props) => props.theme.border.radius.sm};
background-color: ${(props) => props.theme.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
color: ${(props) => props.theme.text};
&:focus {
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
outline: none !important;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.section-header {
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.muted};

View File

@@ -1,7 +1,16 @@
import Modal from 'components/Modal/index';
import classnames from 'classnames';
import React, { useState } from 'react';
import { IconSettings, IconPalette, IconBrowser, IconUserCircle, IconKeyboard, IconZoomQuestion, IconSquareLetterB } from '@tabler/icons';
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { updateActivePreferencesTab } from 'providers/ReduxStore/slices/app';
import {
IconSettings,
IconPalette,
IconBrowser,
IconUserCircle,
IconKeyboard,
IconZoomQuestion,
IconSquareLetterB
} from '@tabler/icons';
import Support from './Support';
import General from './General';
@@ -13,8 +22,13 @@ import Beta from './Beta';
import StyledWrapper from './StyledWrapper';
const Preferences = ({ onClose }) => {
const [tab, setTab] = useState('general');
const Preferences = () => {
const dispatch = useDispatch();
const tab = useSelector((state) => state.app.activePreferencesTab);
const setTab = (tab) => {
dispatch(updateActivePreferencesTab({ tab }));
};
const getTabClassname = (tabName) => {
return classnames(`tab select-none ${tabName}`, {
@@ -25,27 +39,27 @@ const Preferences = ({ onClose }) => {
const getTabPanel = (tab) => {
switch (tab) {
case 'general': {
return <General close={onClose} />;
return <General />;
}
case 'themes': {
return <Themes close={onClose} />;
return <Themes />;
}
case 'proxy': {
return <Proxy close={onClose} />;
return <Proxy />;
}
case 'display': {
return <Display close={onClose} />;
return <Display />;
}
case 'keybindings': {
return <Keybindings close={onClose} />;
return <Keybindings />;
}
case 'beta': {
return <Beta close={onClose} />;
return <Beta />;
}
case 'support': {
@@ -55,42 +69,47 @@ const Preferences = ({ onClose }) => {
};
return (
<StyledWrapper>
<Modal size="lg" title="Preferences" handleCancel={onClose} hideFooter={true}>
<div className="flex flex-row gap-2 mx-[-1rem] !my-[-1.5rem] py-2">
<div className="flex flex-col items-center tabs" role="tablist">
<div className={getTabClassname('general')} role="tab" onClick={() => setTab('general')}>
<IconSettings size={16} strokeWidth={1.5} />
General
</div>
<div className={getTabClassname('themes')} role="tab" onClick={() => setTab('themes')}>
<IconPalette size={16} strokeWidth={1.5} />
Themes
</div>
<div className={getTabClassname('display')} role="tab" onClick={() => setTab('display')}>
<IconBrowser size={16} strokeWidth={1.5} />
Display
</div>
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
<IconUserCircle size={16} strokeWidth={1.5} />
Proxy
</div>
<div className={getTabClassname('keybindings')} role="tab" onClick={() => setTab('keybindings')}>
<IconKeyboard size={16} strokeWidth={1.5} />
Keybindings
</div>
<div className={getTabClassname('support')} role="tab" onClick={() => setTab('support')}>
<IconZoomQuestion size={16} strokeWidth={1.5} />
Support
</div>
<div className={getTabClassname('beta')} role="tab" onClick={() => setTab('beta')}>
<IconSquareLetterB size={16} strokeWidth={1.5} />
Beta
</div>
<StyledWrapper className="h-full">
<div className="flex flex-row gap-2 h-full">
<div className="flex flex-col items-center tabs tablist" role="tablist">
<div className={getTabClassname('general')} role="tab" onClick={() => setTab('general')}>
<IconSettings size={16} strokeWidth={1.5} />
General
</div>
<div className={getTabClassname('themes')} role="tab" onClick={() => setTab('themes')}>
<IconPalette size={16} strokeWidth={1.5} />
Themes
</div>
<div className={getTabClassname('display')} role="tab" onClick={() => setTab('display')}>
<IconBrowser size={16} strokeWidth={1.5} />
Display
</div>
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
<IconUserCircle size={16} strokeWidth={1.5} />
Proxy
</div>
<div className={getTabClassname('keybindings')} role="tab" onClick={() => setTab('keybindings')}>
<IconKeyboard size={16} strokeWidth={1.5} />
Keybindings
</div>
<div className={getTabClassname('support')} role="tab" onClick={() => setTab('support')}>
<IconZoomQuestion size={16} strokeWidth={1.5} />
Support
</div>
<div className={getTabClassname('beta')} role="tab" onClick={() => setTab('beta')}>
<IconSquareLetterB size={16} strokeWidth={1.5} />
Beta
</div>
<section className="flex flex-grow ps-2 pe-4 pt-2 pb-6 tab-panel">{getTabPanel(tab)}</section>
</div>
</Modal>
<section
className="flex flex-grow ps-2 pe-4 pt-2 pb-6 p-[12px] tab-panel"
role="tabpanel"
id={`${tab}-panel`}
aria-labelledby={`${tab}-tab`}
>
{getTabPanel(tab)}
</section>
</div>
</StyledWrapper>
);
};

View File

@@ -33,6 +33,7 @@ import WSResponsePane from 'components/ResponsePane/WsResponsePane';
import { useTabPaneBoundaries } from 'hooks/useTabPaneBoundaries/index';
import ResponseExample from 'components/ResponseExample';
import WorkspaceHome from 'components/WorkspaceHome';
import Preferences from 'components/Preferences';
import EnvironmentSettings from 'components/Environments/EnvironmentSettings';
import GlobalEnvironmentSettings from 'components/Environments/GlobalEnvironmentSettings';
@@ -171,13 +172,17 @@ const RequestTabPanel = () => {
}, [isConsoleOpen, isVerticalLayout]);
if (!activeTabUid || !focusedTab) {
return <WorkspaceHome />;
return <div className="pb-4 px-4">An error occurred!</div>;
}
if (focusedTab.type === 'global-environment-settings') {
return <GlobalEnvironmentSettings />;
}
if (focusedTab.type === 'preferences') {
return <Preferences />;
}
if (!focusedTab.uid || !focusedTab.collectionUid) {
return <div className="pb-4 px-4">An error occurred!</div>;
}

View File

@@ -12,6 +12,10 @@ import ActionIcon from 'ui/ActionIcon';
const CollectionToolBar = ({ collection }) => {
const dispatch = useDispatch();
if (!collection) {
return null;
}
const handleRun = () => {
dispatch(
addTab({

View File

@@ -61,6 +61,14 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
</>
);
}
case 'preferences': {
return (
<>
<IconSettings size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
<span className="ml-1 tab-name">Preferences</span>
</>
);
}
}
};

View File

@@ -172,7 +172,7 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi
setShowConfirmGlobalEnvironmentClose(true);
};
if (['collection-settings', 'collection-overview', 'folder-settings', 'variables', 'collection-runner', 'environment-settings', 'global-environment-settings'].includes(tab.type)) {
if (['collection-settings', 'collection-overview', 'folder-settings', 'variables', 'collection-runner', 'environment-settings', 'global-environment-settings', 'preferences'].includes(tab.type)) {
return (
<StyledWrapper
className={`flex items-center justify-between tab-container px-2 ${tab.preview ? 'italic' : ''}`}

View File

@@ -44,7 +44,7 @@ const RequestTabs = () => {
}, []);
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
const activeCollection = find(collections, (c) => c.uid === activeTab?.collectionUid);
const activeCollection = find(collections, (c) => c?.uid === activeTab?.collectionUid);
const collectionRequestTabs = filter(tabs, (t) => t.collectionUid === activeTab?.collectionUid);
useEffect(() => {
@@ -52,7 +52,7 @@ const RequestTabs = () => {
const checkOverflow = () => {
if (tabsRef.current && scrollContainerRef.current) {
const hasOverflow = tabsRef.current.scrollWidth > scrollContainerRef.current.clientWidth;
const hasOverflow = tabsRef.current.scrollWidth > scrollContainerRef.current.clientWidth + 1;
setShowChevrons(hasOverflow);
}
};
@@ -111,7 +111,7 @@ const RequestTabs = () => {
)}
{collectionRequestTabs && collectionRequestTabs.length ? (
<>
<CollectionToolBar collection={activeCollection} />
{activeCollection && <CollectionToolBar collection={activeCollection} />}
<div className="flex items-center gap-2 pl-2" ref={collectionTabsRef}>
<div className={classnames('scroll-chevrons', { hidden: !showChevrons })}>
<ActionIcon size="lg" onClick={leftSlide} aria-label="Left Chevron" style={{ marginBottom: '3px' }}>

View File

@@ -1,22 +1,29 @@
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import find from 'lodash/find';
import { IconSettings, IconCookie, IconTool, IconSearch, IconPalette, IconBrandGithub } from '@tabler/icons';
import Mousetrap from 'mousetrap';
import { getKeyBindingsForActionAllOS } from 'providers/Hotkeys/keyMappings';
import ToolHint from 'components/ToolHint';
import Preferences from 'components/Preferences';
import Cookies from 'components/Cookies';
import Notifications from 'components/Notifications';
import Portal from 'components/Portal';
import ThemeDropdown from './ThemeDropdown';
import { showPreferences } from 'providers/ReduxStore/slices/app';
import { openConsole } from 'providers/ReduxStore/slices/logs';
import { setActiveWorkspaceTab } from 'providers/ReduxStore/slices/workspaceTabs';
import { addTab } from 'providers/ReduxStore/slices/tabs';
import { useApp } from 'providers/App';
import StyledWrapper from './StyledWrapper';
const StatusBar = () => {
const dispatch = useDispatch();
const preferencesOpen = useSelector((state) => state.app.showPreferences);
const activeWorkspaceUid = useSelector((state) => state.workspaces.activeWorkspaceUid);
const showHomePage = useSelector((state) => state.app.showHomePage);
const showManageWorkspacePage = useSelector((state) => state.app.showManageWorkspacePage);
const showApiSpecPage = useSelector((state) => state.app.showApiSpecPage);
const tabs = useSelector((state) => state.tabs.tabs);
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
const logs = useSelector((state) => state.logs.logs);
const [cookiesOpen, setCookiesOpen] = useState(false);
const { version } = useApp();
@@ -27,6 +34,22 @@ const StatusBar = () => {
dispatch(openConsole());
};
const handlePreferencesClick = () => {
if (showHomePage || showManageWorkspacePage || showApiSpecPage || !activeTabUid) {
if (activeWorkspaceUid) {
dispatch(setActiveWorkspaceTab({ workspaceUid: activeWorkspaceUid, type: 'preferences' }));
}
} else {
dispatch(
addTab({
type: 'preferences',
uid: activeTab?.collectionUid ? `${activeTab.collectionUid}-preferences` : 'preferences',
collectionUid: activeTab?.collectionUid
})
);
}
};
const openGlobalSearch = () => {
const bindings = getKeyBindingsForActionAllOS('globalSearch') || [];
bindings.forEach((binding) => {
@@ -36,21 +59,6 @@ const StatusBar = () => {
return (
<StyledWrapper>
{preferencesOpen && (
<Portal>
<Preferences
onClose={() => {
dispatch(showPreferences(false));
document.querySelector('[data-trigger="preferences"]').focus();
}}
aria-modal="true"
role="dialog"
aria-labelledby="preferences-title"
aria-describedby="preferences-description"
/>
</Portal>
)}
{cookiesOpen && (
<Portal>
<Cookies
@@ -73,7 +81,7 @@ const StatusBar = () => {
<button
className="status-bar-button preferences-button"
data-trigger="preferences"
onClick={() => dispatch(showPreferences(true))}
onClick={handlePreferencesClick}
tabIndex={0}
aria-label="Open Preferences"
>

View File

@@ -22,7 +22,7 @@ const StyledWrapper = styled.div`
table tr {
display: contents;
}
table th {
position: relative;
font-weight: 400;

View File

@@ -7,6 +7,7 @@ import toast from 'react-hot-toast';
import CloseWorkspace from 'components/Sidebar/CloseWorkspace';
import WorkspaceOverview from './WorkspaceOverview';
import WorkspaceEnvironments from './WorkspaceEnvironments';
import Preferences from 'components/Preferences';
import WorkspaceTabs from 'components/WorkspaceTabs';
import StyledWrapper from './StyledWrapper';
import Dropdown from 'components/Dropdown';
@@ -157,6 +158,8 @@ const WorkspaceHome = () => {
return <WorkspaceOverview workspace={activeWorkspace} />;
case 'environments':
return <WorkspaceEnvironments workspace={activeWorkspace} />;
case 'preferences':
return <Preferences />;
default:
return null;
}

View File

@@ -1,12 +1,13 @@
import React from 'react';
import { IconX, IconHome, IconWorld } from '@tabler/icons';
import { IconX, IconHome, IconWorld, IconSettings } from '@tabler/icons';
import { useDispatch } from 'react-redux';
import { closeWorkspaceTab } from 'providers/ReduxStore/slices/workspaceTabs';
import StyledWrapper from './StyledWrapper';
const TAB_ICONS = {
overview: IconHome,
environments: IconWorld
environments: IconWorld,
preferences: IconSettings
};
const WorkspaceTab = ({ tab, isActive }) => {

View File

@@ -57,7 +57,7 @@ const WorkspaceTabs = ({ workspaceUid }) => {
const checkOverflow = () => {
if (tabsRef.current && scrollContainerRef.current) {
const hasOverflow = tabsRef.current.scrollWidth > scrollContainerRef.current.clientWidth;
const hasOverflow = tabsRef.current.scrollWidth > scrollContainerRef.current.clientWidth + 1;
setShowChevrons(hasOverflow);
}
};

View File

@@ -123,7 +123,7 @@ export default function Main() {
<ApiSpecPanel key={activeApiSpecUid} />
) : showManageWorkspacePage ? (
<ManageWorkspace />
) : showHomePage ? (
) : showHomePage || !activeTabUid ? (
<WorkspaceHome />
) : (
<>

View File

@@ -1,10 +1,15 @@
import { useEffect } from 'react';
import {
showPreferences,
updateCookies,
updatePreferences,
updateSystemProxyEnvVariables
} from 'providers/ReduxStore/slices/app';
import {
addTab
} from 'providers/ReduxStore/slices/tabs';
import {
setActiveWorkspaceTab
} from 'providers/ReduxStore/slices/workspaceTabs';
import {
brunoConfigUpdateEvent,
collectionAddDirectoryEvent,
@@ -26,7 +31,7 @@ import { collectionAddEnvFileEvent, openCollectionEvent, hydrateCollectionWithUi
import { workspaceOpenedEvent, workspaceConfigUpdatedEvent } from 'providers/ReduxStore/slices/workspaces/actions';
import { workspaceDotEnvUpdateEvent } from 'providers/ReduxStore/slices/workspaces';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import { useDispatch, useStore } from 'react-redux';
import { isElectron } from 'utils/common/platform';
import { globalEnvironmentsUpdateEvent, updateGlobalEnvironments } from 'providers/ReduxStore/slices/global-environments';
import { collectionAddOauth2CredentialsByUrl, updateCollectionLoadingState } from 'providers/ReduxStore/slices/collections/index';
@@ -36,6 +41,7 @@ import { apiSpecAddFileEvent, apiSpecChangeFileEvent } from 'providers/ReduxStor
const useIpcEvents = () => {
const dispatch = useDispatch();
const store = useStore();
useEffect(() => {
if (!isElectron()) {
@@ -126,7 +132,7 @@ const useIpcEvents = () => {
});
const removeWorkspaceEnvironmentAddedListener = ipcRenderer.on('main:workspace-environment-added', (workspaceUid, file) => {
const state = window.__store__.getState();
const state = store.getState();
const activeWorkspaceUid = state.workspaces?.activeWorkspaceUid;
if (activeWorkspaceUid === workspaceUid) {
const workspace = state.workspaces?.workspaces?.find((w) => w.uid === workspaceUid);
@@ -144,7 +150,7 @@ const useIpcEvents = () => {
});
const removeWorkspaceEnvironmentChangedListener = ipcRenderer.on('main:workspace-environment-changed', (workspaceUid, file) => {
const state = window.__store__.getState();
const state = store.getState();
const activeWorkspaceUid = state.workspaces?.activeWorkspaceUid;
if (activeWorkspaceUid === workspaceUid) {
const workspace = state.workspaces?.workspaces?.find((w) => w.uid === workspaceUid);
@@ -162,7 +168,7 @@ const useIpcEvents = () => {
});
const removeWorkspaceEnvironmentDeletedListener = ipcRenderer.on('main:workspace-environment-deleted', (workspaceUid, environmentUid) => {
const state = window.__store__.getState();
const state = store.getState();
const activeWorkspaceUid = state.workspaces?.activeWorkspaceUid;
if (activeWorkspaceUid === workspaceUid) {
const workspace = state.workspaces?.workspaces?.find((w) => w.uid === workspaceUid);
@@ -239,7 +245,26 @@ const useIpcEvents = () => {
);
const removeShowPreferencesListener = ipcRenderer.on('main:open-preferences', () => {
dispatch(showPreferences(true));
const state = store.getState();
const activeWorkspaceUid = state.workspaces?.activeWorkspaceUid;
const { showHomePage, showManageWorkspacePage, showApiSpecPage } = state.app;
const tabs = state.tabs?.tabs;
const activeTabUid = state.tabs?.activeTabUid;
const activeTab = tabs?.find((t) => t.uid === activeTabUid);
if (showHomePage || showManageWorkspacePage || showApiSpecPage || !activeTabUid) {
if (activeWorkspaceUid) {
dispatch(setActiveWorkspaceTab({ workspaceUid: activeWorkspaceUid, type: 'preferences' }));
}
} else {
dispatch(
addTab({
type: 'preferences',
uid: activeTab?.collectionUid ? `${activeTab.collectionUid}-preferences` : 'preferences',
collectionUid: activeTab?.collectionUid
})
);
}
});
const removePreferencesUpdatesListener = ipcRenderer.on('main:load-preferences', (val) => {

View File

@@ -15,6 +15,7 @@ import {
} from 'providers/ReduxStore/slices/collections/actions';
import { findCollectionByUid, findItemInCollection } from 'utils/collections';
import { addTab, closeTabs, reorderTabs, switchTab } from 'providers/ReduxStore/slices/tabs';
import { closeWorkspaceTab } from 'providers/ReduxStore/slices/workspaceTabs';
import { toggleSidebarCollapse } from 'providers/ReduxStore/slices/app';
import { getKeyBindingsForActionAllOS } from './keyMappings';
@@ -25,6 +26,8 @@ export const HotkeysProvider = (props) => {
const tabs = useSelector((state) => state.tabs.tabs);
const collections = useSelector((state) => state.collections.collections);
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
const showHomePage = useSelector((state) => state.app.showHomePage);
const activeWorkspaceTabUid = useSelector((state) => state.workspaceTabs.activeTabUid);
const [showNewRequestModal, setShowNewRequestModal] = useState(false);
const [showGlobalSearchModal, setShowGlobalSearchModal] = useState(false);
@@ -171,11 +174,15 @@ export const HotkeysProvider = (props) => {
// close tab hotkey
useEffect(() => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('closeTab')], (e) => {
dispatch(
closeTabs({
tabUids: [activeTabUid]
})
);
if (showHomePage && activeWorkspaceTabUid) {
dispatch(closeWorkspaceTab({ uid: activeWorkspaceTabUid }));
} else if (activeTabUid) {
dispatch(
closeTabs({
tabUids: [activeTabUid]
})
);
}
return false; // this stops the event bubbling
});
@@ -183,7 +190,7 @@ export const HotkeysProvider = (props) => {
return () => {
Mousetrap.unbind([...getKeyBindingsForActionAllOS('closeTab')]);
};
}, [activeTabUid]);
}, [activeTabUid, showHomePage, activeWorkspaceTabUid]);
// Switch to the previous tab
useEffect(() => {

View File

@@ -1,7 +1,7 @@
import { createSlice } from '@reduxjs/toolkit';
import filter from 'lodash/filter';
import brunoClipboard from 'utils/bruno-clipboard';
import { addTab, focusTab } from './tabs';
import { addTab, focusTab, closeTabs } from './tabs';
const initialState = {
isDragging: false,
@@ -10,11 +10,11 @@ const initialState = {
sidebarCollapsed: false,
screenWidth: 500,
showHomePage: false,
showPreferences: false,
showApiSpecPage: false,
showManageWorkspacePage: false,
isEnvironmentSettingsModalOpen: false,
isGlobalEnvironmentSettingsModalOpen: false,
activePreferencesTab: 'general',
preferences: {
request: {
sslVerification: true,
@@ -88,18 +88,17 @@ export const appSlice = createSlice({
},
showApiSpecPage: (state) => {
state.showHomePage = false;
state.showPreferences = false;
state.showApiSpecPage = true;
},
hideApiSpecPage: (state) => {
state.showApiSpecPage = false;
},
showPreferences: (state, action) => {
state.showPreferences = action.payload;
},
updatePreferences: (state, action) => {
state.preferences = action.payload;
},
updateActivePreferencesTab: (state, action) => {
state.activePreferencesTab = action.payload.tab;
},
updateCookies: (state, action) => {
state.cookies = action.payload;
},
@@ -156,8 +155,8 @@ export const {
hideManageWorkspacePage,
showApiSpecPage,
hideApiSpecPage,
showPreferences,
updatePreferences,
updateActivePreferencesTab,
updateCookies,
insertTaskIntoQueue,
removeTaskFromQueue,

View File

@@ -25,7 +25,8 @@ export const tabsSlice = createSlice({
'variables',
'collection-runner',
'environment-settings',
'global-environment-settings'
'global-environment-settings',
'preferences'
];
const existingTab = find(state.tabs, (tab) => tab.uid === uid);

View File

@@ -164,11 +164,16 @@ export const workspaceTabsSlice = createSlice({
if (!tab) {
const newTabUid = `${workspaceUid}-${type}`;
const labels = {
overview: 'Overview',
environments: 'Global Environments',
preferences: 'Preferences'
};
const newTab = {
uid: newTabUid,
workspaceUid,
type,
label: type === 'overview' ? 'Overview' : type,
label: labels[type] || type,
permanent: false
};
state.tabs.push(newTab);

View File

@@ -115,6 +115,9 @@ export const findParentItemInCollectionByPathname = (collection, pathname) => {
};
export const findItemInCollection = (collection, itemUid) => {
if (!collection || !collection.items) {
return null;
}
let flattenedItems = flattenItems(collection.items);
return findItem(flattenedItems, itemUid);