fix: path for newly added collection & remove option for outside collections (#6331)

* fixes

* fixes

* fix
This commit is contained in:
naman-bruno
2025-12-06 18:43:53 +05:30
committed by GitHub
parent 3e5ae613f5
commit 4ffb447c53
7 changed files with 83 additions and 56 deletions

View File

@@ -6,6 +6,7 @@ import StyledWrapper from './StyledWrapper';
import CreateOrOpenCollection from './CreateOrOpenCollection';
import CollectionSearch from './CollectionSearch/index';
import { useMemo } from 'react';
import { normalizePath } from 'utils/common/path';
const Collections = ({ showSearch }) => {
const [searchText, setSearchText] = useState('');
@@ -18,7 +19,7 @@ const Collections = ({ showSearch }) => {
const workspaceCollections = useMemo(() => {
if (!activeWorkspace) return [];
return collections.filter((c) =>
activeWorkspace.collections?.some((wc) => wc.path === c.pathname)
activeWorkspace.collections?.some((wc) => normalizePath(wc.path) === normalizePath(c.pathname))
);
}, [activeWorkspace, collections]);

View File

@@ -12,6 +12,7 @@ import RenameCollection from 'components/Sidebar/Collections/Collection/RenameCo
import ShareCollection from 'components/ShareCollection';
import StyledWrapper from './StyledWrapper';
import { mountCollection } from 'providers/ReduxStore/slices/collections/actions';
import { normalizePath } from 'utils/common/path';
const WorkspaceCollections = ({ workspace, onImportCollection }) => {
const dispatch = useDispatch();
@@ -46,7 +47,7 @@ const WorkspaceCollections = ({ workspace, onImportCollection }) => {
const result = [];
workspace.collections.forEach((wc) => {
const loadedCollection = collections.find((c) => c.pathname === wc.path);
const loadedCollection = collections.find((c) => normalizePath(c.pathname) === normalizePath(wc.path));
if (loadedCollection) {
result.push({
@@ -146,14 +147,13 @@ const WorkspaceCollections = ({ workspace, onImportCollection }) => {
if (!collectionToRemove) return;
try {
const collectionInfo = getCollectionWorkspaceInfo(collectionToRemove);
const isDelete = collectionInfo.isInternal && !collectionInfo.isGitBacked;
await dispatch(removeCollectionFromWorkspaceAction(workspace.uid, collectionToRemove.pathname));
const collectionInfo = getCollectionWorkspaceInfo(collectionToRemove);
if (collectionInfo.isLoaded && !collectionInfo.isGitBacked) {
if (isDelete) {
toast.success(`Deleted "${collectionToRemove.name}" collection`);
} else if (collectionInfo.isGitBacked) {
toast.success(`Removed git-backed collection "${collectionToRemove.name}" from workspace`);
} else {
toast.success(`Removed "${collectionToRemove.name}" from workspace`);
}
@@ -165,23 +165,32 @@ const WorkspaceCollections = ({ workspace, onImportCollection }) => {
}
};
const isInternalCollection = (collection) => {
if (!workspace.pathname || !collection.pathname) return false;
const workspaceCollectionsFolder = normalizePath(`${workspace.pathname}/collections`);
const collectionPath = normalizePath(collection.pathname);
return collectionPath.startsWith(workspaceCollectionsFolder);
};
const getCollectionWorkspaceInfo = (collection) => {
if (collection.hasOwnProperty('isGitBacked')) {
return {
isGitBacked: collection.isGitBacked,
gitRemoteUrl: collection.gitRemoteUrl,
isLoaded: collection.isLoaded !== false
isLoaded: collection.isLoaded !== false,
isInternal: isInternalCollection(collection)
};
}
const workspaceCollection = workspace.collections?.find((wc) => {
return collection.pathname === wc.path;
return normalizePath(collection.pathname) === normalizePath(wc.path);
});
return {
isGitBacked: !!workspaceCollection?.remote,
gitRemoteUrl: workspaceCollection?.remote,
isLoaded: true
isLoaded: true,
isInternal: isInternalCollection(collection)
};
};
@@ -222,32 +231,31 @@ const WorkspaceCollections = ({ workspace, onImportCollection }) => {
/>
)}
{collectionToRemove && (
<Modal
size="sm"
title="Delete Collection"
handleCancel={() => setCollectionToRemove(null)}
handleConfirm={confirmRemoveCollection}
confirmText="Delete Collection"
cancelText="Cancel"
style="new"
>
<p className="text-gray-600 dark:text-gray-300">
Are you sure you want to delete <strong>"{collectionToRemove.name}"</strong>?
</p>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-3">
{(() => {
const collectionInfo = getCollectionWorkspaceInfo(collectionToRemove);
{collectionToRemove && (() => {
const collectionInfo = getCollectionWorkspaceInfo(collectionToRemove);
const isDelete = collectionInfo.isInternal && !collectionInfo.isGitBacked;
if (collectionInfo.isGitBacked) {
return 'This will remove the git-backed collection reference from workspace.yml. Local files (if any) will not be deleted.';
} else {
return 'This will permanently delete the collection files from the workspace collections folder.';
}
})()}
</p>
</Modal>
)}
return (
<Modal
size="sm"
title={isDelete ? 'Delete Collection' : 'Remove Collection'}
handleCancel={() => setCollectionToRemove(null)}
handleConfirm={confirmRemoveCollection}
confirmText={isDelete ? 'Delete' : 'Remove'}
cancelText="Cancel"
style="new"
>
<p className="text-gray-600 dark:text-gray-300">
Are you sure you want to {isDelete ? 'delete' : 'remove'} <strong>"{collectionToRemove.name}"</strong>?
</p>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-3">
{isDelete
? 'This will permanently delete the collection files from the workspace collections folder.'
: 'This will remove the collection from the workspace. The collection files will not be deleted.'}
</p>
</Modal>
);
})()}
<div className="h-full overflow-auto">
{workspaceCollections.length === 0 ? (

View File

@@ -7,7 +7,7 @@ import find from 'lodash/find';
import get from 'lodash/get';
import set from 'lodash/set';
import trim from 'lodash/trim';
import path from 'utils/common/path';
import path, { normalizePath } from 'utils/common/path';
import { insertTaskIntoQueue, toggleSidebarCollapse } from 'providers/ReduxStore/slices/app';
import toast from 'react-hot-toast';
import {
@@ -2278,7 +2278,6 @@ export const openCollectionEvent = (uid, pathname, brunoConfig) => (dispatch, ge
.validate(collection)
.then(() => dispatch(_createCollection({ ...collection, securityConfig })))
.then(() => {
// Expand sidebar if it's collapsed after collection is successfully opened
const state = getState();
if (state.app.sidebarCollapsed) {
dispatch(toggleSidebarCollapse());
@@ -2286,7 +2285,9 @@ export const openCollectionEvent = (uid, pathname, brunoConfig) => (dispatch, ge
const activeWorkspace = state.workspaces.workspaces.find((w) => w.uid === state.workspaces.activeWorkspaceUid);
if (activeWorkspace) {
const isAlreadyInWorkspace = activeWorkspace.collections?.some((c) => c.path === pathname);
const isAlreadyInWorkspace = activeWorkspace.collections?.some(
(c) => normalizePath(c.path) === normalizePath(pathname)
);
if (!isAlreadyInWorkspace) {
const workspaceCollection = {
@@ -2294,8 +2295,6 @@ export const openCollectionEvent = (uid, pathname, brunoConfig) => (dispatch, ge
path: pathname
};
// The electron handler will automatically trigger workspace config update
// which will cause the app to react and reload collections
ipcRenderer
.invoke('renderer:add-collection-to-workspace', activeWorkspace.pathname, workspaceCollection)
.catch((err) => {

View File

@@ -12,6 +12,7 @@ import { showHomePage } from '../app';
import { createCollection, openCollection, openMultipleCollections } from '../collections/actions';
import { removeCollection } from '../collections';
import { updateGlobalEnvironments } from '../global-environments';
import { normalizePath } from 'utils/common/path';
import toast from 'react-hot-toast';
const { ipcRenderer } = window;
@@ -127,7 +128,11 @@ export const removeCollectionFromWorkspaceAction = (workspaceUid, collectionPath
throw new Error('Workspace not found');
}
const collection = collectionsState.collections.find((c) => c.pathname === collectionPath);
const normalizedCollectionPath = normalizePath(collectionPath);
const collection = collectionsState.collections.find(
(c) => normalizePath(c.pathname) === normalizedCollectionPath
);
await ipcRenderer.invoke('renderer:remove-collection-from-workspace',
workspaceUid,
@@ -135,8 +140,9 @@ export const removeCollectionFromWorkspaceAction = (workspaceUid, collectionPath
collectionPath);
if (collection) {
const workspaceCollection = workspace.collections?.find((wc) =>
wc.path === collectionPath);
const workspaceCollection = workspace.collections?.find(
(wc) => normalizePath(wc.path) === normalizedCollectionPath
);
if (workspaceCollection) {
dispatch(removeCollection({ collectionUid: collection.uid }));
@@ -161,15 +167,17 @@ const loadWorkspaceCollectionsForSwitch = async (dispatch, workspace) => {
};
try {
const workspaceCollections = await dispatch(loadWorkspaceCollections(workspace.uid));
await dispatch(loadWorkspaceCollections(workspace.uid));
const updatedWorkspace = await dispatch((_, getState) => getState().workspaces.workspaces.find((w) => w.uid === workspace.uid));
if (updatedWorkspace?.collections?.length > 0) {
const alreadyOpenCollections = await dispatch((_, getState) => getState().collections.collections.map((c) => c.pathname));
const alreadyOpenCollections = await dispatch((_, getState) =>
getState().collections.collections.map((c) => normalizePath(c.pathname))
);
const collectionPaths = updatedWorkspace.collections
.map((wc) => wc.path)
.filter((p) => p && !alreadyOpenCollections.includes(p));
.filter((p) => p && !alreadyOpenCollections.includes(normalizePath(p)));
if (collectionPaths.length > 0) {
await openCollectionsFunction(collectionPaths, updatedWorkspace.pathname);
@@ -222,7 +230,7 @@ export const loadWorkspaceCollections = (workspaceUid, force = false) => {
const hasProcessedCollections = workspace.collections
&& workspace.collections.length > 0
&& workspace.collections.some((c) => c.path && c.path.startsWith('/'));
&& workspace.collections.some((c) => c.path && path.isAbsolute(c.path));
if (!force && hasProcessedCollections) {
return workspace.collections;
@@ -342,12 +350,12 @@ export const workspaceConfigUpdatedEvent = (workspacePath, workspaceUid, workspa
await dispatch(loadWorkspaceCollections(workspaceUid, true));
const workspace = getState().workspaces.workspaces.find((w) => w.uid === workspaceUid);
const openCollections = getState().collections.collections.map((c) => c.pathname);
const openCollections = getState().collections.collections.map((c) => normalizePath(c.pathname));
if (workspace?.collections?.length > 0) {
const newCollectionPaths = workspace.collections
.map((workspaceCollection) => workspaceCollection.path)
.filter((collectionPath) => collectionPath && !openCollections.includes(collectionPath));
.filter((collectionPath) => collectionPath && !openCollections.includes(normalizePath(collectionPath)));
if (newCollectionPaths.length > 0) {
try {

View File

@@ -1,4 +1,5 @@
import { createSlice } from '@reduxjs/toolkit';
import { normalizePath } from 'utils/common/path';
const DEFAULT_WORKSPACE_UID = 'default';
@@ -61,9 +62,11 @@ export const workspacesSlice = createSlice({
const { workspaceUid, collectionLocation } = action.payload;
const workspace = state.workspaces.find((w) => w.uid === workspaceUid);
if (workspace?.collections) {
// Filter by both path and location since path could be relative or absolute
workspace.collections = workspace.collections.filter((c) =>
c.path !== collectionLocation && c.location !== collectionLocation);
const normalizedLocation = normalizePath(collectionLocation);
workspace.collections = workspace.collections.filter((c) => {
const normalizedPath = normalizePath(c.path);
return normalizedPath !== normalizedLocation;
});
}
},

View File

@@ -163,5 +163,10 @@ const getAbsoluteFilePath = (basePath, relativePath, shouldPosixify = false) =>
return shouldPosixify ? posixify(result) : result;
};
const normalizePath = (p) => {
if (!p) return '';
return p.replace(/\\/g, '/').replace(/\/+$/, '');
};
export default brunoPath;
export { getRelativePath, getBasename, getAbsoluteFilePath };
export { getRelativePath, getBasename, getAbsoluteFilePath, normalizePath };

View File

@@ -11,7 +11,11 @@ const makeRelativePath = (workspacePath, absolutePath) => {
}
try {
return path.relative(workspacePath, absolutePath);
const relativePath = path.relative(workspacePath, absolutePath);
if (relativePath.startsWith('..') && relativePath.split(path.sep).filter((s) => s === '..').length > 2) {
return absolutePath;
}
return relativePath;
} catch (error) {
return absolutePath;
}
@@ -196,12 +200,11 @@ const getWorkspaceCollections = (workspacePath) => {
const config = readWorkspaceConfig(workspacePath);
const collections = config.collections || [];
// Resolve relative paths to absolute
return collections.map((collection) => {
if (collection.path && !path.isAbsolute(collection.path)) {
return {
...collection,
path: path.join(workspacePath, collection.path)
path: path.resolve(workspacePath, collection.path)
};
}
return collection;