mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-15 03:41:28 +00:00
fix: path for newly added collection & remove option for outside collections (#6331)
* fixes * fixes * fix
This commit is contained in:
@@ -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]);
|
||||
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user