Skip to content

Commit 9736436

Browse files
committed
add dekstop storage
1 parent 42a15e0 commit 9736436

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
2+
import type {
3+
WorkspacesStorage,
4+
WorkspacesStateData,
5+
} from '../services/workspaces-storage';
6+
import { createServiceLocator } from '@mongodb-js/compass-app-registry';
7+
8+
export type WorkspacesStorageServiceState = {
9+
status: 'INITIAL' | 'LOADING' | 'READY' | 'ERROR';
10+
error: Error | null;
11+
workspaces: WorkspacesStateData | null;
12+
};
13+
14+
export const noopWorkspacesStorageService: WorkspacesStorageServiceState &
15+
WorkspacesStorage = {
16+
status: 'INITIAL',
17+
error: null,
18+
workspaces: null,
19+
save: () => {
20+
return Promise.resolve(false);
21+
},
22+
load: () => {
23+
return Promise.resolve(null);
24+
},
25+
};
26+
27+
const WorkspacesStorageServiceContext = React.createContext<
28+
WorkspacesStorageServiceState & WorkspacesStorage
29+
>(noopWorkspacesStorageService);
30+
31+
export const WorkspacesStorageServiceProvider: React.FunctionComponent<{
32+
storage: WorkspacesStorage;
33+
}> = ({ storage, children }) => {
34+
const storageRef = useRef(storage);
35+
const [serviceState, setServiceState] =
36+
useState<WorkspacesStorageServiceState>({
37+
status: 'INITIAL',
38+
error: null,
39+
workspaces: null,
40+
});
41+
const service = useMemo(() => {
42+
return {
43+
...serviceState,
44+
async save(toSave: WorkspacesStateData) {
45+
setServiceState((prevState) => {
46+
return {
47+
...prevState,
48+
workspaces: toSave,
49+
};
50+
});
51+
return storageRef.current.save(toSave);
52+
},
53+
async load() {
54+
setServiceState((prevState) => {
55+
return {
56+
...prevState,
57+
status: 'LOADING',
58+
};
59+
});
60+
try {
61+
const workspaces = await storageRef.current.load();
62+
setServiceState((prevState) => {
63+
return { ...prevState, workspaces, error: null, status: 'READY' };
64+
});
65+
return workspaces;
66+
} catch (err) {
67+
setServiceState((prevState) => {
68+
return { ...prevState, error: err as Error, status: 'ERROR' };
69+
});
70+
throw err;
71+
}
72+
},
73+
};
74+
}, [serviceState]);
75+
76+
const serviceRef = useRef(service);
77+
78+
useEffect(() => {
79+
void serviceRef.current.load().catch(() => {
80+
// handled by the method
81+
});
82+
}, []);
83+
84+
return (
85+
<WorkspacesStorageServiceContext.Provider value={service}>
86+
{children}
87+
</WorkspacesStorageServiceContext.Provider>
88+
);
89+
};
90+
91+
export function useSavedWorkspaces() {
92+
const { status, error, workspaces } = useContext(
93+
WorkspacesStorageServiceContext
94+
);
95+
return { status, error, workspaces };
96+
}
97+
98+
export const workspacesStorageServiceLocator = createServiceLocator(() => {
99+
const service = useContext(WorkspacesStorageServiceContext);
100+
return {
101+
save: service.save.bind(service),
102+
load: service.load.bind(service),
103+
};
104+
}, 'workspacesStorageServiceLocator');
105+
106+
export type WorkspacesStorageService = ReturnType<
107+
typeof workspacesStorageServiceLocator
108+
>;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
import type {
3+
WorkspacesStateData,
4+
WorkspacesStorage,
5+
} from './workspaces-storage';
6+
import { FileUserData } from '@mongodb-js/compass-user-data';
7+
import { WorkspacesStateSchema } from './workspaces-storage';
8+
import { EJSON } from 'bson';
9+
import { WorkspacesStorageServiceProvider } from '../provider';
10+
11+
class WorkspacesStorageDesktop implements WorkspacesStorage {
12+
private userData = new FileUserData(
13+
WorkspacesStateSchema,
14+
'WorkspacesState',
15+
{
16+
serialize: (content) => EJSON.stringify(content, undefined, 2),
17+
deserialize: (content: string) => EJSON.parse(content),
18+
}
19+
);
20+
save(state: WorkspacesStateData): Promise<boolean> {
21+
return this.userData.write('current-workspace', state);
22+
}
23+
load(): Promise<WorkspacesStateData | null> {
24+
return this.userData
25+
.readOne('current-workspace', {
26+
ignoreErrors: true,
27+
})
28+
.then((data) => data ?? null);
29+
}
30+
}
31+
32+
const storage = new WorkspacesStorageDesktop();
33+
34+
export const WorkspacesStorageServiceProviderDesktop: React.FunctionComponent =
35+
({ children }) => {
36+
return (
37+
<WorkspacesStorageServiceProvider storage={storage}>
38+
{children}
39+
</WorkspacesStorageServiceProvider>
40+
);
41+
};
42+
43+
export default storage;

packages/compass-workspaces/src/services/workspaces-storage-web.tsx

Whitespace-only changes.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { z } from '@mongodb-js/compass-user-data';
2+
3+
/**
4+
* Schema for saving workspace tab state to user data
5+
*/
6+
7+
// Define schema for collection subtab
8+
const CollectionSubtabSchema = z.enum([
9+
'Documents',
10+
'Aggregations',
11+
'Schema',
12+
'Indexes',
13+
'Validation',
14+
'GlobalWrites',
15+
]);
16+
17+
// Define schema for workspace tab type
18+
const WorkspaceTabTypeSchema = z.enum([
19+
'Welcome',
20+
'My Queries',
21+
'Data Modeling',
22+
'Shell',
23+
'Databases',
24+
'Performance',
25+
'Collections',
26+
'Collection',
27+
]);
28+
29+
// Define schema for a workspace tab
30+
export const WorkspaceTabSchema = z.object({
31+
id: z.string(),
32+
type: WorkspaceTabTypeSchema,
33+
connectionId: z.string().optional(),
34+
namespace: z.string().optional(),
35+
initialQuery: z.record(z.any()).optional(),
36+
initialAggregation: z.record(z.any()).optional(),
37+
initialPipeline: z.array(z.record(z.any())).optional(),
38+
initialPipelineText: z.string().optional(),
39+
editViewName: z.string().optional(),
40+
initialEvaluate: z.union([z.string(), z.array(z.string())]).optional(),
41+
initialInput: z.string().optional(),
42+
subTab: CollectionSubtabSchema.optional(),
43+
});
44+
45+
// Define schema for the complete workspaces state
46+
export const WorkspacesStateSchema = z.object({
47+
tabs: z.array(WorkspaceTabSchema),
48+
activeTabId: z.string().nullable(),
49+
timestamp: z.number(),
50+
});
51+
52+
// TypeScript types derived from the schemas
53+
export type WorkspaceTabData = z.output<typeof WorkspaceTabSchema>;
54+
export type WorkspacesStateData = z.output<typeof WorkspacesStateSchema>;
55+
56+
export interface WorkspacesStorage {
57+
save(state: WorkspacesStateData): Promise<boolean>;
58+
load(): Promise<WorkspacesStateData | null>;
59+
}

0 commit comments

Comments
 (0)