Skip to content

Commit 68b9e53

Browse files
committed
init
1 parent c832b30 commit 68b9e53

File tree

5 files changed

+403
-1
lines changed

5 files changed

+403
-1
lines changed

packages/compass-workspaces/src/index.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ import workspacesReducer, {
2020
import Workspaces from './components';
2121
import { applyMiddleware, createStore } from 'redux';
2222
import thunk from 'redux-thunk';
23+
import {
24+
workspacesStateChangeMiddleware,
25+
loadWorkspaceStateFromUserData,
26+
convertSavedStateToInitialTabs,
27+
} from './stores/workspaces-middleware';
2328
import type { MongoDBInstance } from '@mongodb-js/compass-app-stores/provider';
2429
import { mongoDBInstancesManagerLocator } from '@mongodb-js/compass-app-stores/provider';
2530
import type Collection from 'mongodb-collection-model';
@@ -66,12 +71,47 @@ export function configureStore(
6671
collectionInfo: {},
6772
databaseInfo: {},
6873
},
69-
applyMiddleware(thunk.withExtraArgument(services))
74+
applyMiddleware(
75+
thunk.withExtraArgument(services),
76+
workspacesStateChangeMiddleware
77+
)
7078
);
7179

7280
return store;
7381
}
7482

83+
/**
84+
* Configures the store with optional state restoration from UserData
85+
*/
86+
export async function configureStoreWithStateRestoration(
87+
initialWorkspaceTabs: OpenWorkspaceOptions[] | undefined | null,
88+
services: WorkspacesServices,
89+
restoreFromUserData = false
90+
) {
91+
let tabsToUse = initialWorkspaceTabs;
92+
93+
// If restoration is enabled and no initial tabs provided, try to restore from UserData
94+
if (
95+
restoreFromUserData &&
96+
(!initialWorkspaceTabs || initialWorkspaceTabs.length === 0)
97+
) {
98+
try {
99+
const savedState = await loadWorkspaceStateFromUserData();
100+
if (savedState && savedState.tabs.length > 0) {
101+
// Convert saved state back to initial tabs format
102+
tabsToUse = convertSavedStateToInitialTabs(
103+
savedState
104+
) as OpenWorkspaceOptions[];
105+
}
106+
} catch (error) {
107+
services.logger?.debug('Workspace State Restoration', { error });
108+
// Continue with original initialWorkspaceTabs (empty or provided)
109+
}
110+
}
111+
112+
return configureStore(tabsToUse, services);
113+
}
114+
75115
export function activateWorkspacePlugin(
76116
{
77117
initialWorkspaceTabs,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Workspaces Middleware
2+
3+
This middleware allows you to perform actions/callbacks whenever the workspaces state changes, such as when tabs are opened, closed, or modified.
4+
5+
## How it works
6+
7+
The middleware intercepts every Redux action and compares the previous and next workspaces state. If the state has actually changed, it calls a single callback function with the new state and the action that caused the change.
8+
9+
## Integration
10+
11+
The middleware is already integrated into the Redux store in `index.ts`:
12+
13+
```typescript
14+
import { workspacesStateChangeMiddleware } from './stores/workspaces-middleware';
15+
16+
// In configureStore function:
17+
applyMiddleware(
18+
thunk.withExtraArgument(services),
19+
workspacesStateChangeMiddleware
20+
);
21+
```
22+
23+
## Customization
24+
25+
To implement your custom logic, edit the callback function in `workspaces-middleware.ts`:
26+
27+
```typescript
28+
function onWorkspacesStateChange(newState: WorkspacesState, action: AnyAction) {
29+
// TODO: Implement your callback logic here
30+
// You have access to:
31+
// - newState: The complete new workspaces state
32+
// - action: The Redux action that triggered the change
33+
}
34+
```
35+
36+
The callback receives:
37+
38+
- `newState`: The complete new `WorkspacesState` object
39+
- `action`: The Redux action that caused the state change
40+
41+
## Example Use Cases
42+
43+
- **Save state**: Persist the entire workspaces state to user data
44+
- **Analytics**: Track workspace usage and state changes
45+
- **Auto-save**: Save workspace configuration whenever it changes
46+
- **Window title**: Update browser title based on active tab
47+
- **URL sync**: Update browser URL to reflect current workspace
48+
- **Logging**: Log all workspace state changes for debugging
49+
50+
## Guaranteed Behavior
51+
52+
- The callback is **only called when the state actually changes** (strict reference equality check)
53+
- The callback receives the **complete new state**, so you can access all tabs, active tab, and metadata
54+
- The callback is called **after** the state has been updated but **before** the action completes
55+
56+
See `workspaces-middleware-example.ts` for detailed implementation examples.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* EXAMPLE USAGE OF WORKSPACES MIDDLEWARE
3+
*
4+
* This file shows how you can implement custom logic in the single callback.
5+
* You can copy this implementation into the workspaces-middleware.ts file and
6+
* customize it for your specific needs.
7+
*/
8+
9+
import type { AnyAction } from 'redux';
10+
import type { WorkspacesState } from './workspaces';
11+
12+
/**
13+
* Example implementation for the workspaces state change callback
14+
*/
15+
function exampleOnWorkspacesStateChange(
16+
newState: WorkspacesState,
17+
action: AnyAction
18+
) {
19+
// Example: Save the entire state to persistent storage
20+
// userDataService.saveWorkspacesState(newState);
21+
22+
// Example: Send analytics about current workspace state
23+
// analyticsService.track('workspaces_state_changed', {
24+
// action_type: action.type,
25+
// total_tabs: newState.tabs.length,
26+
// active_tab_type: getActiveTabType(newState),
27+
// timestamp: new Date().toISOString(),
28+
// });
29+
30+
// Example: Update window title based on active tab
31+
updateWindowTitle(newState);
32+
33+
// Example: Auto-save current workspace configuration
34+
// configService.saveWorkspaceLayout(newState);
35+
36+
// Example: Update browser history or URL based on active tab
37+
// updateBrowserState(newState);
38+
39+
console.log('Workspaces state changed:', {
40+
actionType: action.type,
41+
totalTabs: newState.tabs.length,
42+
activeTabId: newState.activeTabId,
43+
activeTabType: getActiveTabType(newState),
44+
timestamp: new Date().toISOString(),
45+
});
46+
}
47+
48+
/**
49+
* Helper function to get the type of the currently active tab
50+
*/
51+
function getActiveTabType(state: WorkspacesState): string | null {
52+
const activeTab = state.tabs.find((tab) => tab.id === state.activeTabId);
53+
return activeTab?.type || null;
54+
}
55+
56+
/**
57+
* Helper function to update the window title based on the active tab
58+
*/
59+
function updateWindowTitle(state: WorkspacesState) {
60+
const activeTab = state.tabs.find((tab) => tab.id === state.activeTabId);
61+
62+
if (!activeTab) {
63+
document.title = 'MongoDB Compass';
64+
return;
65+
}
66+
67+
const title = getTabTitle(activeTab);
68+
document.title = `MongoDB Compass - ${title}`;
69+
}
70+
71+
/**
72+
* Helper function to get a human-readable title for a tab
73+
*/
74+
function getTabTitle(tab: WorkspacesState['tabs'][0]): string {
75+
switch (tab.type) {
76+
case 'Welcome':
77+
return 'Welcome';
78+
case 'My Queries':
79+
return 'My Queries';
80+
case 'Shell':
81+
return 'Shell';
82+
case 'Databases':
83+
return 'Databases';
84+
case 'Performance':
85+
return 'Performance';
86+
case 'Collections':
87+
return `Collections - ${'namespace' in tab ? tab.namespace : ''}`;
88+
case 'Collection':
89+
return `${'namespace' in tab ? tab.namespace : ''} - ${
90+
'subTab' in tab ? tab.subTab : 'Documents'
91+
}`;
92+
default:
93+
return 'MongoDB Compass';
94+
}
95+
}

0 commit comments

Comments
 (0)