Files
lemma/app/src/contexts/WorkspaceContext.tsx
2025-05-18 16:57:48 +02:00

245 lines
6.7 KiB
TypeScript

import type {
ReactNode} from 'react';
import React, {
createContext,
useContext,
useState,
useEffect,
useCallback
} from 'react';
import type { MantineColorScheme} from '@mantine/core';
import { useMantineColorScheme } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import {
getLastWorkspaceName,
getWorkspace,
updateWorkspace,
updateLastWorkspaceName,
deleteWorkspace,
listWorkspaces,
} from '@/api/workspace';
import type { Workspace} from '@/types/workspace';
import { DEFAULT_WORKSPACE_SETTINGS } from '@/types/workspace';
interface WorkspaceContextType {
currentWorkspace: Workspace | null;
workspaces: Workspace[];
settings: Workspace | typeof DEFAULT_WORKSPACE_SETTINGS;
updateSettings: (newSettings: Partial<Workspace>) => Promise<void>;
loading: boolean;
colorScheme: MantineColorScheme;
updateColorScheme: (newTheme: MantineColorScheme) => void;
switchWorkspace: (workspaceName: string) => Promise<void>;
deleteCurrentWorkspace: () => Promise<void>;
}
const WorkspaceContext = createContext<WorkspaceContextType | null>(null);
interface WorkspaceProviderProps {
children: ReactNode;
}
export const WorkspaceProvider: React.FC<WorkspaceProviderProps> = ({
children,
}) => {
const [currentWorkspace, setCurrentWorkspace] = useState<Workspace | null>(
null
);
const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const { colorScheme, setColorScheme } = useMantineColorScheme();
const loadWorkspaces = useCallback(async (): Promise<Workspace[]> => {
try {
const workspaceList = await listWorkspaces();
setWorkspaces(workspaceList);
return workspaceList;
} catch (error) {
console.error('Failed to load workspaces:', error);
notifications.show({
title: 'Error',
message: 'Failed to load workspaces list',
color: 'red',
});
return [];
}
}, []);
const loadWorkspaceData = useCallback(
async (workspaceName: string): Promise<void> => {
try {
const workspace = await getWorkspace(workspaceName);
setCurrentWorkspace(workspace);
setColorScheme(workspace.theme as MantineColorScheme);
} catch (error) {
console.error('Failed to load workspace data:', error);
notifications.show({
title: 'Error',
message: 'Failed to load workspace data',
color: 'red',
});
}
},
[]
);
const loadFirstAvailableWorkspace = useCallback(async (): Promise<void> => {
try {
const allWorkspaces = await listWorkspaces();
if (allWorkspaces.length > 0) {
const firstWorkspace = allWorkspaces[0];
if (!firstWorkspace) throw new Error('No workspaces available');
await updateLastWorkspaceName(firstWorkspace.name);
await loadWorkspaceData(firstWorkspace.name);
}
} catch (error) {
console.error('Failed to load first available workspace:', error);
notifications.show({
title: 'Error',
message: 'Failed to load workspace',
color: 'red',
});
}
}, []);
useEffect(() => {
const initializeWorkspace = async (): Promise<void> => {
try {
const lastWorkspaceName = await getLastWorkspaceName();
if (lastWorkspaceName) {
await loadWorkspaceData(lastWorkspaceName);
} else {
await loadFirstAvailableWorkspace();
}
await loadWorkspaces();
} catch (error) {
console.error('Failed to initialize workspace:', error);
await loadFirstAvailableWorkspace();
} finally {
setLoading(false);
}
};
initializeWorkspace();
}, []);
const switchWorkspace = useCallback(
async (workspaceName: string): Promise<void> => {
try {
setLoading(true);
await updateLastWorkspaceName(workspaceName);
await loadWorkspaceData(workspaceName);
await loadWorkspaces();
} catch (error) {
console.error('Failed to switch workspace:', error);
notifications.show({
title: 'Error',
message: 'Failed to switch workspace',
color: 'red',
});
} finally {
setLoading(false);
}
},
[]
);
const deleteCurrentWorkspace = useCallback(async (): Promise<void> => {
if (!currentWorkspace) return;
try {
const allWorkspaces = await loadWorkspaces();
if (allWorkspaces.length <= 1) {
notifications.show({
title: 'Error',
message:
'Cannot delete the last workspace. At least one workspace must exist.',
color: 'red',
});
return;
}
// Delete workspace and get the next workspace ID
const nextWorkspaceName: string = await deleteWorkspace(
currentWorkspace.name
);
// Load the new workspace data
await loadWorkspaceData(nextWorkspaceName);
notifications.show({
title: 'Success',
message: 'Workspace deleted successfully',
color: 'green',
});
await loadWorkspaces();
} catch (error) {
console.error('Failed to delete workspace:', error);
notifications.show({
title: 'Error',
message: 'Failed to delete workspace',
color: 'red',
});
}
}, [currentWorkspace]);
const updateSettings = useCallback(
async (newSettings: Partial<Workspace>): Promise<void> => {
if (!currentWorkspace) return;
try {
const updatedWorkspace = {
...currentWorkspace,
...newSettings,
};
const response = await updateWorkspace(
currentWorkspace.name,
updatedWorkspace
);
setCurrentWorkspace(response);
setColorScheme(response.theme);
await loadWorkspaces();
} catch (error) {
console.error('Failed to save settings:', error);
throw error;
}
},
[currentWorkspace, setColorScheme]
);
const updateColorScheme = useCallback(
(newTheme: MantineColorScheme): void => {
setColorScheme(newTheme);
},
[setColorScheme]
);
const value: WorkspaceContextType = {
currentWorkspace,
workspaces,
settings: currentWorkspace || DEFAULT_WORKSPACE_SETTINGS,
updateSettings,
loading,
colorScheme,
updateColorScheme,
switchWorkspace,
deleteCurrentWorkspace,
};
return (
<WorkspaceContext.Provider value={value}>
{children}
</WorkspaceContext.Provider>
);
};
export const useWorkspace = (): WorkspaceContextType => {
const context = useContext(WorkspaceContext);
if (!context) {
throw new Error('useWorkspace must be used within a WorkspaceProvider');
}
return context;
};