mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 15:44:21 +00:00
Refactor workspace context usage to improve structure and introduce ThemeContext
This commit is contained in:
@@ -5,7 +5,7 @@ import { EditorView, keymap } from '@codemirror/view';
|
||||
import { markdown } from '@codemirror/lang-markdown';
|
||||
import { defaultKeymap } from '@codemirror/commands';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { useWorkspace } from '../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../hooks/useWorkspace';
|
||||
|
||||
interface EditorProps {
|
||||
content: string;
|
||||
|
||||
@@ -9,7 +9,7 @@ import rehypePrism from 'rehype-prism';
|
||||
import * as prod from 'react/jsx-runtime';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { remarkWikiLinks } from '../../utils/remarkWikiLinks';
|
||||
import { useWorkspace } from '../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../hooks/useWorkspace';
|
||||
|
||||
interface MarkdownPreviewProps {
|
||||
content: string;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
IconGitCommit,
|
||||
} from '@tabler/icons-react';
|
||||
import { useModalContext } from '../../contexts/ModalContext';
|
||||
import { useWorkspace } from '../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../hooks/useWorkspace';
|
||||
|
||||
interface FileActionsProps {
|
||||
handlePullChanges: () => Promise<boolean>;
|
||||
|
||||
@@ -5,7 +5,7 @@ import Sidebar from './Sidebar';
|
||||
import MainContent from './MainContent';
|
||||
import { useFileNavigation } from '../../hooks/useFileNavigation';
|
||||
import { useFileList } from '../../hooks/useFileList';
|
||||
import { useWorkspace } from '../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../hooks/useWorkspace';
|
||||
|
||||
const Layout: React.FC = () => {
|
||||
const { currentWorkspace, loading: workspaceLoading } = useWorkspace();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Box } from '@mantine/core';
|
||||
import FileActions from '../files/FileActions';
|
||||
import FileTree from '../files/FileTree';
|
||||
import { useGitOperations } from '../../hooks/useGitOperations';
|
||||
import { useWorkspace } from '../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../hooks/useWorkspace';
|
||||
import type { FileNode } from '@/types/models';
|
||||
|
||||
interface SidebarProps {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
useMantineTheme,
|
||||
} from '@mantine/core';
|
||||
import { IconFolders, IconSettings, IconFolderPlus } from '@tabler/icons-react';
|
||||
import { useWorkspace } from '../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../hooks/useWorkspace';
|
||||
import { useModalContext } from '../../contexts/ModalContext';
|
||||
import { listWorkspaces } from '../../api/workspace';
|
||||
import CreateWorkspaceModal from '../modals/workspace/CreateWorkspaceModal';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Text, Switch, Group, Box } from '@mantine/core';
|
||||
import { useWorkspace } from '../../../contexts/WorkspaceContext';
|
||||
import { useTheme } from '../../../contexts/ThemeContext';
|
||||
import { Theme } from '@/types/models';
|
||||
|
||||
interface AppearanceSettingsProps {
|
||||
@@ -10,7 +10,7 @@ interface AppearanceSettingsProps {
|
||||
const AppearanceSettings: React.FC<AppearanceSettingsProps> = ({
|
||||
onThemeChange,
|
||||
}) => {
|
||||
const { colorScheme, updateColorScheme } = useWorkspace();
|
||||
const { colorScheme, updateColorScheme } = useTheme();
|
||||
|
||||
const handleThemeChange = (): void => {
|
||||
const newTheme = colorScheme === 'dark' ? Theme.Light : Theme.Dark;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Button } from '@mantine/core';
|
||||
import DeleteWorkspaceModal from '../../modals/workspace/DeleteWorkspaceModal';
|
||||
import { useWorkspace } from '../../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../../hooks/useWorkspace';
|
||||
import { useModalContext } from '../../../contexts/ModalContext';
|
||||
|
||||
const DangerZoneSettings: React.FC = () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Accordion,
|
||||
} from '@mantine/core';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { useWorkspace } from '../../../contexts/WorkspaceContext';
|
||||
import { useWorkspace } from '../../../hooks/useWorkspace';
|
||||
import AppearanceSettings from './AppearanceSettings';
|
||||
import EditorSettings from './EditorSettings';
|
||||
import GitSettings from './GitSettings';
|
||||
|
||||
46
app/src/contexts/ThemeContext.tsx
Normal file
46
app/src/contexts/ThemeContext.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useCallback,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { useMantineColorScheme, type MantineColorScheme } from '@mantine/core';
|
||||
|
||||
interface ThemeContextType {
|
||||
colorScheme: MantineColorScheme;
|
||||
updateColorScheme: (newTheme: MantineColorScheme) => void;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextType | null>(null);
|
||||
|
||||
interface ThemeProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
||||
const { colorScheme, setColorScheme } = useMantineColorScheme();
|
||||
|
||||
const updateColorScheme = useCallback(
|
||||
(newTheme: MantineColorScheme): void => {
|
||||
setColorScheme(newTheme);
|
||||
},
|
||||
[setColorScheme]
|
||||
);
|
||||
|
||||
const value: ThemeContextType = {
|
||||
colorScheme,
|
||||
updateColorScheme,
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTheme = (): ThemeContextType => {
|
||||
const context = useContext(ThemeContext);
|
||||
if (!context) {
|
||||
throw new Error('useTheme must be used within a ThemeProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,241 +1,22 @@
|
||||
import React, {
|
||||
type ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { useMantineColorScheme, type MantineColorScheme } from '@mantine/core';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { DEFAULT_WORKSPACE_SETTINGS, type Workspace } from '@/types/models';
|
||||
import {
|
||||
deleteWorkspace,
|
||||
getLastWorkspaceName,
|
||||
getWorkspace,
|
||||
listWorkspaces,
|
||||
updateLastWorkspaceName,
|
||||
updateWorkspace,
|
||||
} from '@/api/workspace';
|
||||
import React from 'react';
|
||||
import { ThemeProvider } from './ThemeContext';
|
||||
import { WorkspaceDataProvider } from './WorkspaceDataContext';
|
||||
import { useWorkspace as useWorkspaceHook } from '../hooks/useWorkspace';
|
||||
|
||||
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);
|
||||
// Re-export the useWorkspace hook directly for backward compatibility
|
||||
export const useWorkspace = useWorkspaceHook;
|
||||
|
||||
interface WorkspaceProviderProps {
|
||||
children: ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
// Create a backward-compatible WorkspaceProvider that composes our new providers
|
||||
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',
|
||||
});
|
||||
}
|
||||
},
|
||||
[setColorScheme]
|
||||
);
|
||||
|
||||
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',
|
||||
});
|
||||
}
|
||||
}, [loadWorkspaceData]);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
void initializeWorkspace();
|
||||
}, [loadFirstAvailableWorkspace, loadWorkspaceData, loadWorkspaces]);
|
||||
|
||||
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);
|
||||
}
|
||||
},
|
||||
[loadWorkspaceData, loadWorkspaces]
|
||||
);
|
||||
|
||||
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, loadWorkspaceData, loadWorkspaces]);
|
||||
|
||||
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, loadWorkspaces, 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>
|
||||
<ThemeProvider>
|
||||
<WorkspaceDataProvider>{children}</WorkspaceDataProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useWorkspace = (): WorkspaceContextType => {
|
||||
const context = useContext(WorkspaceContext);
|
||||
if (!context) {
|
||||
throw new Error('useWorkspace must be used within a WorkspaceProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
146
app/src/contexts/WorkspaceDataContext.tsx
Normal file
146
app/src/contexts/WorkspaceDataContext.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import React, {
|
||||
type ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { DEFAULT_WORKSPACE_SETTINGS, type Workspace } from '@/types/models';
|
||||
import {
|
||||
getWorkspace,
|
||||
listWorkspaces,
|
||||
getLastWorkspaceName,
|
||||
updateLastWorkspaceName,
|
||||
} from '@/api/workspace';
|
||||
import { useTheme } from './ThemeContext';
|
||||
|
||||
interface WorkspaceDataContextType {
|
||||
currentWorkspace: Workspace | null;
|
||||
workspaces: Workspace[];
|
||||
settings: Workspace | typeof DEFAULT_WORKSPACE_SETTINGS;
|
||||
loading: boolean;
|
||||
loadWorkspaces: () => Promise<Workspace[]>;
|
||||
loadWorkspaceData: (workspaceName: string) => Promise<void>;
|
||||
setCurrentWorkspace: (workspace: Workspace | null) => void;
|
||||
}
|
||||
|
||||
const WorkspaceDataContext = createContext<WorkspaceDataContextType | null>(
|
||||
null
|
||||
);
|
||||
|
||||
interface WorkspaceDataProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const WorkspaceDataProvider: React.FC<WorkspaceDataProviderProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [currentWorkspace, setCurrentWorkspace] = useState<Workspace | null>(
|
||||
null
|
||||
);
|
||||
const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const { updateColorScheme } = useTheme();
|
||||
|
||||
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);
|
||||
updateColorScheme(workspace.theme);
|
||||
} catch (error) {
|
||||
console.error('Failed to load workspace data:', error);
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: 'Failed to load workspace data',
|
||||
color: 'red',
|
||||
});
|
||||
}
|
||||
},
|
||||
[updateColorScheme]
|
||||
);
|
||||
|
||||
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',
|
||||
});
|
||||
}
|
||||
}, [loadWorkspaceData]);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
void initializeWorkspace();
|
||||
}, [loadFirstAvailableWorkspace, loadWorkspaceData, loadWorkspaces]);
|
||||
|
||||
const value: WorkspaceDataContextType = {
|
||||
currentWorkspace,
|
||||
workspaces,
|
||||
settings: currentWorkspace || DEFAULT_WORKSPACE_SETTINGS,
|
||||
loading,
|
||||
loadWorkspaces,
|
||||
loadWorkspaceData,
|
||||
setCurrentWorkspace,
|
||||
};
|
||||
|
||||
return (
|
||||
<WorkspaceDataContext.Provider value={value}>
|
||||
{children}
|
||||
</WorkspaceDataContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useWorkspaceData = (): WorkspaceDataContextType => {
|
||||
const context = useContext(WorkspaceDataContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useWorkspaceData must be used within a WorkspaceDataProvider'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { isImageFile } from '../utils/fileHelpers';
|
||||
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
import { getFileContent } from '@/api/file';
|
||||
import { DEFAULT_FILE } from '@/types/models';
|
||||
|
||||
@@ -16,7 +16,7 @@ interface UseFileContentResult {
|
||||
export const useFileContent = (
|
||||
selectedFile: string | null
|
||||
): UseFileContentResult => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentWorkspace } = useWorkspaceData();
|
||||
const [content, setContent] = useState<string>(DEFAULT_FILE.content);
|
||||
const [originalContent, setOriginalContent] = useState<string>(
|
||||
DEFAULT_FILE.content
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { listFiles } from '../api/file';
|
||||
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
import type { FileNode } from '@/types/models';
|
||||
|
||||
interface UseFileListResult {
|
||||
@@ -10,7 +10,7 @@ interface UseFileListResult {
|
||||
|
||||
export const useFileList = (): UseFileListResult => {
|
||||
const [files, setFiles] = useState<FileNode[]>([]);
|
||||
const { currentWorkspace, loading: workspaceLoading } = useWorkspace();
|
||||
const { currentWorkspace, loading: workspaceLoading } = useWorkspaceData();
|
||||
|
||||
const loadFileList = useCallback(async (): Promise<void> => {
|
||||
if (!currentWorkspace || workspaceLoading) return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
import { useLastOpenedFile } from './useLastOpenedFile';
|
||||
import { DEFAULT_FILE } from '@/types/models';
|
||||
|
||||
@@ -12,7 +12,7 @@ interface UseFileNavigationResult {
|
||||
export const useFileNavigation = (): UseFileNavigationResult => {
|
||||
const [selectedFile, setSelectedFile] = useState<string>(DEFAULT_FILE.path);
|
||||
const [isNewFile, setIsNewFile] = useState<boolean>(true);
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentWorkspace } = useWorkspaceData();
|
||||
const { loadLastOpenedFile, saveLastOpenedFile } = useLastOpenedFile();
|
||||
|
||||
const handleFileSelect = useCallback(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { saveFile, deleteFile } from '../api/file';
|
||||
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
import { useGitOperations } from './useGitOperations';
|
||||
import { FileAction } from '@/types/models';
|
||||
|
||||
@@ -12,7 +12,7 @@ interface UseFileOperationsResult {
|
||||
}
|
||||
|
||||
export const useFileOperations = (): UseFileOperationsResult => {
|
||||
const { currentWorkspace, settings } = useWorkspace();
|
||||
const { currentWorkspace, settings } = useWorkspaceData();
|
||||
const { handleCommitAndPush } = useGitOperations();
|
||||
|
||||
const autoCommit = useCallback(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { pullChanges, commitAndPush } from '../api/git';
|
||||
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
import type { CommitHash } from '@/types/models';
|
||||
|
||||
interface UseGitOperationsResult {
|
||||
@@ -10,7 +10,7 @@ interface UseGitOperationsResult {
|
||||
}
|
||||
|
||||
export const useGitOperations = (): UseGitOperationsResult => {
|
||||
const { currentWorkspace, settings } = useWorkspace();
|
||||
const { currentWorkspace, settings } = useWorkspaceData();
|
||||
|
||||
const handlePull = useCallback(async (): Promise<boolean> => {
|
||||
if (!currentWorkspace || !settings.gitEnabled) return false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback } from 'react';
|
||||
import { getLastOpenedFile, updateLastOpenedFile } from '../api/file';
|
||||
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
|
||||
interface UseLastOpenedFileResult {
|
||||
loadLastOpenedFile: () => Promise<string | null>;
|
||||
@@ -8,7 +8,7 @@ interface UseLastOpenedFileResult {
|
||||
}
|
||||
|
||||
export const useLastOpenedFile = (): UseLastOpenedFileResult => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentWorkspace } = useWorkspaceData();
|
||||
|
||||
const loadLastOpenedFile = useCallback(async (): Promise<string | null> => {
|
||||
if (!currentWorkspace) return null;
|
||||
|
||||
39
app/src/hooks/useWorkspace.ts
Normal file
39
app/src/hooks/useWorkspace.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { useWorkspaceOperations } from './useWorkspaceOperations';
|
||||
import type { Workspace } from '@/types/models';
|
||||
import type { MantineColorScheme } from '@mantine/core';
|
||||
|
||||
interface UseWorkspaceResult {
|
||||
currentWorkspace: Workspace | null;
|
||||
workspaces: Workspace[];
|
||||
settings:
|
||||
| Workspace
|
||||
| typeof import('@/types/models').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>;
|
||||
}
|
||||
|
||||
export const useWorkspace = (): UseWorkspaceResult => {
|
||||
const { currentWorkspace, workspaces, settings, loading } =
|
||||
useWorkspaceData();
|
||||
const { colorScheme, updateColorScheme } = useTheme();
|
||||
const { switchWorkspace, deleteCurrentWorkspace, updateSettings } =
|
||||
useWorkspaceOperations();
|
||||
|
||||
return {
|
||||
currentWorkspace,
|
||||
workspaces,
|
||||
settings,
|
||||
updateSettings,
|
||||
loading,
|
||||
colorScheme,
|
||||
updateColorScheme,
|
||||
switchWorkspace,
|
||||
deleteCurrentWorkspace,
|
||||
};
|
||||
};
|
||||
117
app/src/hooks/useWorkspaceOperations.ts
Normal file
117
app/src/hooks/useWorkspaceOperations.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { useCallback } from 'react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { useWorkspaceData } from '../contexts/WorkspaceDataContext';
|
||||
import {
|
||||
updateLastWorkspaceName,
|
||||
updateWorkspace,
|
||||
deleteWorkspace,
|
||||
} from '@/api/workspace';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import type { Workspace } from '@/types/models';
|
||||
|
||||
interface UseWorkspaceOperationsResult {
|
||||
switchWorkspace: (workspaceName: string) => Promise<void>;
|
||||
deleteCurrentWorkspace: () => Promise<void>;
|
||||
updateSettings: (newSettings: Partial<Workspace>) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useWorkspaceOperations = (): UseWorkspaceOperationsResult => {
|
||||
const {
|
||||
currentWorkspace,
|
||||
loadWorkspaceData,
|
||||
loadWorkspaces,
|
||||
setCurrentWorkspace,
|
||||
} = useWorkspaceData();
|
||||
const { updateColorScheme } = useTheme();
|
||||
|
||||
const switchWorkspace = useCallback(
|
||||
async (workspaceName: string): Promise<void> => {
|
||||
try {
|
||||
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',
|
||||
});
|
||||
}
|
||||
},
|
||||
[loadWorkspaceData, loadWorkspaces]
|
||||
);
|
||||
|
||||
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, loadWorkspaceData, loadWorkspaces]);
|
||||
|
||||
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);
|
||||
if (newSettings.theme) {
|
||||
updateColorScheme(response.theme);
|
||||
}
|
||||
await loadWorkspaces();
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[currentWorkspace, loadWorkspaces, updateColorScheme, setCurrentWorkspace]
|
||||
);
|
||||
|
||||
return {
|
||||
switchWorkspace,
|
||||
deleteCurrentWorkspace,
|
||||
updateSettings,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user