mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 16:04:23 +00:00
Add Workspace context
This commit is contained in:
@@ -3,19 +3,13 @@ import { MantineProvider, ColorSchemeScript } from '@mantine/core';
|
|||||||
import { Notifications } from '@mantine/notifications';
|
import { Notifications } from '@mantine/notifications';
|
||||||
import { ModalsProvider } from '@mantine/modals';
|
import { ModalsProvider } from '@mantine/modals';
|
||||||
import Layout from './components/Layout';
|
import Layout from './components/Layout';
|
||||||
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
|
import { WorkspaceProvider } from './contexts/WorkspaceContext';
|
||||||
import { ModalProvider } from './contexts/ModalContext';
|
import { ModalProvider } from './contexts/ModalContext';
|
||||||
import '@mantine/core/styles.css';
|
import '@mantine/core/styles.css';
|
||||||
import '@mantine/notifications/styles.css';
|
import '@mantine/notifications/styles.css';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
const { loading } = useSettings();
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Layout />;
|
return <Layout />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,11 +20,11 @@ function App() {
|
|||||||
<MantineProvider defaultColorScheme="light">
|
<MantineProvider defaultColorScheme="light">
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<ModalsProvider>
|
<ModalsProvider>
|
||||||
<SettingsProvider>
|
<WorkspaceProvider>
|
||||||
<ModalProvider>
|
<ModalProvider>
|
||||||
<AppContent />
|
<AppContent />
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
</SettingsProvider>
|
</WorkspaceProvider>
|
||||||
</ModalsProvider>
|
</ModalsProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import { EditorView, keymap } from '@codemirror/view';
|
|||||||
import { markdown } from '@codemirror/lang-markdown';
|
import { markdown } from '@codemirror/lang-markdown';
|
||||||
import { defaultKeymap } from '@codemirror/commands';
|
import { defaultKeymap } from '@codemirror/commands';
|
||||||
import { oneDark } from '@codemirror/theme-one-dark';
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
const Editor = ({ content, handleContentChange, handleSave, selectedFile }) => {
|
const Editor = ({ content, handleContentChange, handleSave, selectedFile }) => {
|
||||||
const { settings } = useSettings();
|
const { settings } = useWorkspace();
|
||||||
const editorRef = useRef();
|
const editorRef = useRef();
|
||||||
const viewRef = useRef();
|
const viewRef = useRef();
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import {
|
|||||||
IconGitPullRequest,
|
IconGitPullRequest,
|
||||||
IconGitCommit,
|
IconGitCommit,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
|
||||||
import { useModalContext } from '../contexts/ModalContext';
|
import { useModalContext } from '../contexts/ModalContext';
|
||||||
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
const FileActions = ({ handlePullChanges, selectedFile }) => {
|
const FileActions = ({ handlePullChanges, selectedFile }) => {
|
||||||
const { settings } = useSettings();
|
const { settings } = useWorkspace();
|
||||||
const {
|
const {
|
||||||
setNewFileModalVisible,
|
setNewFileModalVisible,
|
||||||
setDeleteFileModalVisible,
|
setDeleteFileModalVisible,
|
||||||
|
|||||||
@@ -1,16 +1,30 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AppShell, Container } from '@mantine/core';
|
import { AppShell, Container, Loader, Center } from '@mantine/core';
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
import Sidebar from './Sidebar';
|
import Sidebar from './Sidebar';
|
||||||
import MainContent from './MainContent';
|
import MainContent from './MainContent';
|
||||||
import { useFileNavigation } from '../hooks/useFileNavigation';
|
import { useFileNavigation } from '../hooks/useFileNavigation';
|
||||||
import { useFileList } from '../hooks/useFileList';
|
import { useFileList } from '../hooks/useFileList';
|
||||||
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
|
const { currentWorkspace, loading: workspaceLoading } = useWorkspace();
|
||||||
const { selectedFile, handleFileSelect, handleLinkClick } =
|
const { selectedFile, handleFileSelect, handleLinkClick } =
|
||||||
useFileNavigation();
|
useFileNavigation();
|
||||||
const { files, loadFileList } = useFileList();
|
const { files, loadFileList } = useFileList();
|
||||||
|
|
||||||
|
if (workspaceLoading) {
|
||||||
|
return (
|
||||||
|
<Center style={{ height: '100vh' }}>
|
||||||
|
<Loader size="xl" />
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentWorkspace) {
|
||||||
|
return <div>No workspace found. Please create a workspace.</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppShell header={{ height: 60 }} padding="md">
|
<AppShell header={{ height: 60 }} padding="md">
|
||||||
<AppShell.Header>
|
<AppShell.Header>
|
||||||
@@ -22,8 +36,8 @@ const Layout = () => {
|
|||||||
p={0}
|
p={0}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
height: 'calc(100vh - 60px - 2rem)', // Subtracting header height and vertical padding
|
height: 'calc(100vh - 60px - 2rem)',
|
||||||
overflow: 'hidden', // Prevent scrolling in the container
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import CommitMessageModal from './modals/CommitMessageModal';
|
|||||||
import { useFileContent } from '../hooks/useFileContent';
|
import { useFileContent } from '../hooks/useFileContent';
|
||||||
import { useFileOperations } from '../hooks/useFileOperations';
|
import { useFileOperations } from '../hooks/useFileOperations';
|
||||||
import { useGitOperations } from '../hooks/useGitOperations';
|
import { useGitOperations } from '../hooks/useGitOperations';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
const MainContent = ({
|
const MainContent = ({
|
||||||
selectedFile,
|
selectedFile,
|
||||||
@@ -19,7 +19,7 @@ const MainContent = ({
|
|||||||
loadFileList,
|
loadFileList,
|
||||||
}) => {
|
}) => {
|
||||||
const [activeTab, setActiveTab] = useState('source');
|
const [activeTab, setActiveTab] = useState('source');
|
||||||
const { settings } = useSettings();
|
const { settings } = useWorkspace();
|
||||||
const {
|
const {
|
||||||
content,
|
content,
|
||||||
hasUnsavedChanges,
|
hasUnsavedChanges,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useReducer, useEffect, useCallback, useRef } from 'react';
|
import React, { useReducer, useEffect, useCallback, useRef } from 'react';
|
||||||
import { Modal, Badge, Button, Group, Title } from '@mantine/core';
|
import { Modal, Badge, Button, Group, Title } from '@mantine/core';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
import AppearanceSettings from './settings/AppearanceSettings';
|
import AppearanceSettings from './settings/AppearanceSettings';
|
||||||
import EditorSettings from './settings/EditorSettings';
|
import EditorSettings from './settings/EditorSettings';
|
||||||
import GitSettings from './settings/GitSettings';
|
import GitSettings from './settings/GitSettings';
|
||||||
@@ -50,7 +50,7 @@ function settingsReducer(state, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
const { settings, updateSettings, colorScheme } = useSettings();
|
const { settings, updateSettings, colorScheme } = useWorkspace();
|
||||||
const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
|
const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
|
||||||
const [state, dispatch] = useReducer(settingsReducer, initialState);
|
const [state, dispatch] = useReducer(settingsReducer, initialState);
|
||||||
const isInitialMount = useRef(true);
|
const isInitialMount = useRef(true);
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { Box } from '@mantine/core';
|
|||||||
import FileActions from './FileActions';
|
import FileActions from './FileActions';
|
||||||
import FileTree from './FileTree';
|
import FileTree from './FileTree';
|
||||||
import { useGitOperations } from '../hooks/useGitOperations';
|
import { useGitOperations } from '../hooks/useGitOperations';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
const Sidebar = ({ selectedFile, handleFileSelect, files, loadFileList }) => {
|
const Sidebar = ({ selectedFile, handleFileSelect, files, loadFileList }) => {
|
||||||
const { settings } = useSettings();
|
const { settings } = useWorkspace();
|
||||||
const { handlePull } = useGitOperations(settings.gitEnabled);
|
const { handlePull } = useGitOperations(settings.gitEnabled);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Switch, Group, Box, Title } from '@mantine/core';
|
import { Text, Switch, Group, Box, Title } from '@mantine/core';
|
||||||
import { useSettings } from '../../contexts/SettingsContext';
|
import { useWorkspace } from '../../contexts/WorkspaceContext';
|
||||||
|
|
||||||
const AppearanceSettings = ({ onThemeChange }) => {
|
const AppearanceSettings = ({ onThemeChange }) => {
|
||||||
const { colorScheme, toggleColorScheme } = useSettings();
|
const { colorScheme, toggleColorScheme } = useWorkspace();
|
||||||
|
|
||||||
const handleThemeChange = () => {
|
const handleThemeChange = () => {
|
||||||
toggleColorScheme();
|
toggleColorScheme();
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
import React, {
|
|
||||||
createContext,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useCallback,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { useMantineColorScheme } from '@mantine/core';
|
|
||||||
import { fetchUserSettings, saveUserSettings } from '../services/api';
|
|
||||||
import { DEFAULT_SETTINGS } from '../utils/constants';
|
|
||||||
|
|
||||||
const SettingsContext = createContext();
|
|
||||||
|
|
||||||
export const useSettings = () => useContext(SettingsContext);
|
|
||||||
|
|
||||||
export const SettingsProvider = ({ children }) => {
|
|
||||||
const { colorScheme, setColorScheme } = useMantineColorScheme();
|
|
||||||
const [settings, setSettings] = useState(DEFAULT_SETTINGS);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const loadSettings = async () => {
|
|
||||||
try {
|
|
||||||
const userSettings = await fetchUserSettings(1);
|
|
||||||
setSettings(userSettings.settings);
|
|
||||||
setColorScheme(userSettings.settings.theme);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load user settings:', error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadSettings();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const updateSettings = useCallback(
|
|
||||||
async (newSettings) => {
|
|
||||||
try {
|
|
||||||
await saveUserSettings({
|
|
||||||
userId: 1,
|
|
||||||
settings: newSettings,
|
|
||||||
});
|
|
||||||
setSettings(newSettings);
|
|
||||||
if (newSettings.theme) {
|
|
||||||
setColorScheme(newSettings.theme);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to save settings:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setColorScheme]
|
|
||||||
);
|
|
||||||
|
|
||||||
const toggleColorScheme = useCallback(() => {
|
|
||||||
const newTheme = colorScheme === 'dark' ? 'light' : 'dark';
|
|
||||||
setColorScheme(newTheme);
|
|
||||||
updateSettings({ ...settings, theme: newTheme });
|
|
||||||
}, [colorScheme, settings, setColorScheme, updateSettings]);
|
|
||||||
|
|
||||||
const contextValue = useMemo(
|
|
||||||
() => ({
|
|
||||||
settings,
|
|
||||||
updateSettings,
|
|
||||||
toggleColorScheme,
|
|
||||||
loading,
|
|
||||||
colorScheme,
|
|
||||||
}),
|
|
||||||
[settings, updateSettings, toggleColorScheme, loading, colorScheme]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsContext.Provider value={contextValue}>
|
|
||||||
{children}
|
|
||||||
</SettingsContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
92
frontend/src/contexts/WorkspaceContext.js
Normal file
92
frontend/src/contexts/WorkspaceContext.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useCallback,
|
||||||
|
} from 'react';
|
||||||
|
import { useMantineColorScheme } from '@mantine/core';
|
||||||
|
import {
|
||||||
|
fetchLastWorkspace,
|
||||||
|
fetchWorkspaceSettings,
|
||||||
|
saveWorkspaceSettings,
|
||||||
|
} from '../services/api';
|
||||||
|
import { DEFAULT_SETTINGS } from '../utils/constants';
|
||||||
|
|
||||||
|
const WorkspaceContext = createContext();
|
||||||
|
|
||||||
|
export const WorkspaceProvider = ({ children }) => {
|
||||||
|
const [currentWorkspace, setCurrentWorkspace] = useState(null);
|
||||||
|
const [settings, setSettings] = useState(DEFAULT_SETTINGS);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const { colorScheme, setColorScheme } = useMantineColorScheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadWorkspaceAndSettings = async () => {
|
||||||
|
try {
|
||||||
|
const workspace = await fetchLastWorkspace();
|
||||||
|
setCurrentWorkspace(workspace);
|
||||||
|
|
||||||
|
if (workspace) {
|
||||||
|
const workspaceSettings = await fetchWorkspaceSettings(workspace.id);
|
||||||
|
setSettings(workspaceSettings.settings);
|
||||||
|
setColorScheme(workspaceSettings.settings.theme);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load workspace or settings:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadWorkspaceAndSettings();
|
||||||
|
}, [setColorScheme]);
|
||||||
|
|
||||||
|
const updateSettings = useCallback(
|
||||||
|
async (newSettings) => {
|
||||||
|
if (!currentWorkspace) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await saveWorkspaceSettings(currentWorkspace.id, newSettings);
|
||||||
|
setSettings(newSettings);
|
||||||
|
if (newSettings.theme) {
|
||||||
|
setColorScheme(newSettings.theme);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save settings:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentWorkspace, setColorScheme]
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggleColorScheme = useCallback(() => {
|
||||||
|
const newTheme = colorScheme === 'dark' ? 'light' : 'dark';
|
||||||
|
setColorScheme(newTheme);
|
||||||
|
updateSettings({ ...settings, theme: newTheme });
|
||||||
|
}, [colorScheme, settings, setColorScheme, updateSettings]);
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
currentWorkspace,
|
||||||
|
setCurrentWorkspace,
|
||||||
|
settings,
|
||||||
|
updateSettings,
|
||||||
|
toggleColorScheme,
|
||||||
|
loading,
|
||||||
|
colorScheme,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WorkspaceContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</WorkspaceContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useWorkspace = () => {
|
||||||
|
const context = useContext(WorkspaceContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useWorkspace must be used within a WorkspaceProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -2,38 +2,45 @@ import { useState, useCallback, useEffect } from 'react';
|
|||||||
import { fetchFileContent } from '../services/api';
|
import { fetchFileContent } from '../services/api';
|
||||||
import { isImageFile } from '../utils/fileHelpers';
|
import { isImageFile } from '../utils/fileHelpers';
|
||||||
import { DEFAULT_FILE } from '../utils/constants';
|
import { DEFAULT_FILE } from '../utils/constants';
|
||||||
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
export const useFileContent = (selectedFile) => {
|
export const useFileContent = (selectedFile) => {
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
const [content, setContent] = useState(DEFAULT_FILE.content);
|
const [content, setContent] = useState(DEFAULT_FILE.content);
|
||||||
const [originalContent, setOriginalContent] = useState(DEFAULT_FILE.content);
|
const [originalContent, setOriginalContent] = useState(DEFAULT_FILE.content);
|
||||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||||||
|
|
||||||
const loadFileContent = useCallback(async (filePath) => {
|
const loadFileContent = useCallback(
|
||||||
try {
|
async (filePath) => {
|
||||||
let newContent;
|
if (!currentWorkspace) return;
|
||||||
if (filePath === DEFAULT_FILE.path) {
|
|
||||||
newContent = DEFAULT_FILE.content;
|
try {
|
||||||
} else if (!isImageFile(filePath)) {
|
let newContent;
|
||||||
newContent = await fetchFileContent(filePath);
|
if (filePath === DEFAULT_FILE.path) {
|
||||||
} else {
|
newContent = DEFAULT_FILE.content;
|
||||||
newContent = ''; // Set empty content for image files
|
} else if (!isImageFile(filePath)) {
|
||||||
|
newContent = await fetchFileContent(currentWorkspace.id, filePath);
|
||||||
|
} else {
|
||||||
|
newContent = ''; // Set empty content for image files
|
||||||
|
}
|
||||||
|
setContent(newContent);
|
||||||
|
setOriginalContent(newContent);
|
||||||
|
setHasUnsavedChanges(false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading file content:', err);
|
||||||
|
setContent(''); // Set empty content on error
|
||||||
|
setOriginalContent('');
|
||||||
|
setHasUnsavedChanges(false);
|
||||||
}
|
}
|
||||||
setContent(newContent);
|
},
|
||||||
setOriginalContent(newContent);
|
[currentWorkspace]
|
||||||
setHasUnsavedChanges(false);
|
);
|
||||||
} catch (err) {
|
|
||||||
console.error('Error loading file content:', err);
|
|
||||||
setContent(''); // Set empty content on error
|
|
||||||
setOriginalContent('');
|
|
||||||
setHasUnsavedChanges(false);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedFile) {
|
if (selectedFile && currentWorkspace) {
|
||||||
loadFileContent(selectedFile);
|
loadFileContent(selectedFile);
|
||||||
}
|
}
|
||||||
}, [selectedFile, loadFileContent]);
|
}, [selectedFile, currentWorkspace, loadFileContent]);
|
||||||
|
|
||||||
const handleContentChange = useCallback(
|
const handleContentChange = useCallback(
|
||||||
(newContent) => {
|
(newContent) => {
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { fetchFileList } from '../services/api';
|
import { fetchFileList } from '../services/api';
|
||||||
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
export const useFileList = () => {
|
export const useFileList = () => {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const loadFileList = useCallback(async () => {
|
const loadFileList = useCallback(async () => {
|
||||||
|
if (!currentWorkspace) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileList = await fetchFileList();
|
const fileList = await fetchFileList(currentWorkspace.id);
|
||||||
if (Array.isArray(fileList)) {
|
if (Array.isArray(fileList)) {
|
||||||
setFiles(fileList);
|
setFiles(fileList);
|
||||||
} else {
|
} else {
|
||||||
@@ -15,7 +19,7 @@ export const useFileList = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load file list:', error);
|
console.error('Failed to load file list:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [currentWorkspace]);
|
||||||
|
|
||||||
return { files, loadFileList };
|
return { files, loadFileList };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { useState, useCallback } from 'react';
|
|||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { lookupFileByName } from '../services/api';
|
import { lookupFileByName } from '../services/api';
|
||||||
import { DEFAULT_FILE } from '../utils/constants';
|
import { DEFAULT_FILE } from '../utils/constants';
|
||||||
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
export const useFileNavigation = () => {
|
export const useFileNavigation = () => {
|
||||||
const [selectedFile, setSelectedFile] = useState(DEFAULT_FILE.path);
|
const [selectedFile, setSelectedFile] = useState(DEFAULT_FILE.path);
|
||||||
const [isNewFile, setIsNewFile] = useState(true);
|
const [isNewFile, setIsNewFile] = useState(true);
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const handleFileSelect = useCallback((filePath) => {
|
const handleFileSelect = useCallback((filePath) => {
|
||||||
setSelectedFile(filePath);
|
setSelectedFile(filePath);
|
||||||
@@ -14,8 +16,10 @@ export const useFileNavigation = () => {
|
|||||||
|
|
||||||
const handleLinkClick = useCallback(
|
const handleLinkClick = useCallback(
|
||||||
async (filename) => {
|
async (filename) => {
|
||||||
|
if (!currentWorkspace) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filePaths = await lookupFileByName(filename);
|
const filePaths = await lookupFileByName(currentWorkspace.id, filename);
|
||||||
if (filePaths.length >= 1) {
|
if (filePaths.length >= 1) {
|
||||||
handleFileSelect(filePaths[0]);
|
handleFileSelect(filePaths[0]);
|
||||||
} else {
|
} else {
|
||||||
@@ -34,7 +38,7 @@ export const useFileNavigation = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleFileSelect]
|
[currentWorkspace, handleFileSelect]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { handleLinkClick, selectedFile, isNewFile, handleFileSelect };
|
return { handleLinkClick, selectedFile, isNewFile, handleFileSelect };
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { saveFileContent, deleteFile } from '../services/api';
|
import { saveFileContent, deleteFile } from '../services/api';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
import { useGitOperations } from './useGitOperations';
|
import { useGitOperations } from './useGitOperations';
|
||||||
|
|
||||||
export const useFileOperations = () => {
|
export const useFileOperations = () => {
|
||||||
const { settings } = useSettings();
|
const { currentWorkspace, settings } = useWorkspace();
|
||||||
const { handleCommitAndPush } = useGitOperations(settings.gitEnabled);
|
const { handleCommitAndPush } = useGitOperations();
|
||||||
|
|
||||||
const autoCommit = useCallback(
|
const autoCommit = useCallback(
|
||||||
async (filePath, action) => {
|
async (filePath, action) => {
|
||||||
@@ -15,7 +15,6 @@ export const useFileOperations = () => {
|
|||||||
.replace('${filename}', filePath)
|
.replace('${filename}', filePath)
|
||||||
.replace('${action}', action);
|
.replace('${action}', action);
|
||||||
|
|
||||||
// Capitalize the first letter of the commit message
|
|
||||||
commitMessage =
|
commitMessage =
|
||||||
commitMessage.charAt(0).toUpperCase() + commitMessage.slice(1);
|
commitMessage.charAt(0).toUpperCase() + commitMessage.slice(1);
|
||||||
|
|
||||||
@@ -27,8 +26,10 @@ export const useFileOperations = () => {
|
|||||||
|
|
||||||
const handleSave = useCallback(
|
const handleSave = useCallback(
|
||||||
async (filePath, content) => {
|
async (filePath, content) => {
|
||||||
|
if (!currentWorkspace) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await saveFileContent(filePath, content);
|
await saveFileContent(currentWorkspace.id, filePath, content);
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
message: 'File saved successfully',
|
message: 'File saved successfully',
|
||||||
@@ -46,13 +47,15 @@ export const useFileOperations = () => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[autoCommit]
|
[currentWorkspace, autoCommit]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDelete = useCallback(
|
const handleDelete = useCallback(
|
||||||
async (filePath) => {
|
async (filePath) => {
|
||||||
|
if (!currentWorkspace) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteFile(filePath);
|
await deleteFile(currentWorkspace.id, filePath);
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
message: 'File deleted successfully',
|
message: 'File deleted successfully',
|
||||||
@@ -70,13 +73,15 @@ export const useFileOperations = () => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[autoCommit]
|
[currentWorkspace, autoCommit]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCreate = useCallback(
|
const handleCreate = useCallback(
|
||||||
async (fileName, initialContent = '') => {
|
async (fileName, initialContent = '') => {
|
||||||
|
if (!currentWorkspace) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await saveFileContent(fileName, initialContent);
|
await saveFileContent(currentWorkspace.id, fileName, initialContent);
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
message: 'File created successfully',
|
message: 'File created successfully',
|
||||||
@@ -94,7 +99,7 @@ export const useFileOperations = () => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[autoCommit]
|
[currentWorkspace, autoCommit]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { handleSave, handleDelete, handleCreate };
|
return { handleSave, handleDelete, handleCreate };
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { pullChanges, commitAndPush } from '../services/api';
|
import { pullChanges, commitAndPush } from '../services/api';
|
||||||
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
|
export const useGitOperations = () => {
|
||||||
|
const { currentWorkspace, settings } = useWorkspace();
|
||||||
|
|
||||||
export const useGitOperations = (gitEnabled) => {
|
|
||||||
const handlePull = useCallback(async () => {
|
const handlePull = useCallback(async () => {
|
||||||
if (!gitEnabled) return false;
|
if (!currentWorkspace || !settings.gitEnabled) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await pullChanges();
|
await pullChanges(currentWorkspace.id);
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
message: 'Successfully pulled latest changes',
|
message: 'Successfully pulled latest changes',
|
||||||
@@ -22,13 +26,14 @@ export const useGitOperations = (gitEnabled) => {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}, [gitEnabled]);
|
}, [currentWorkspace, settings.gitEnabled]);
|
||||||
|
|
||||||
const handleCommitAndPush = useCallback(
|
const handleCommitAndPush = useCallback(
|
||||||
async (message) => {
|
async (message) => {
|
||||||
if (!gitEnabled) return false;
|
if (!currentWorkspace || !settings.gitEnabled) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await commitAndPush(message);
|
await commitAndPush(currentWorkspace.id, message);
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
message: 'Successfully committed and pushed changes',
|
message: 'Successfully committed and pushed changes',
|
||||||
@@ -45,7 +50,7 @@ export const useGitOperations = (gitEnabled) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[gitEnabled]
|
[currentWorkspace, settings.gitEnabled]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { handlePull, handleCommitAndPush };
|
return { handlePull, handleCommitAndPush };
|
||||||
|
|||||||
@@ -16,76 +16,176 @@ const apiCall = async (url, options = {}) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchFileList = async () => {
|
export const fetchLastWorkspace = async () => {
|
||||||
const response = await apiCall(`${API_BASE_URL}/files`);
|
const response = await apiCall(`${API_BASE_URL}/users/1/workspaces/last`);
|
||||||
return response.json();
|
return response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchFileContent = async (filePath) => {
|
export const fetchFileList = async (workspaceId) => {
|
||||||
const response = await apiCall(`${API_BASE_URL}/files/${filePath}`);
|
|
||||||
return response.text();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const saveFileContent = async (filePath, content) => {
|
|
||||||
const response = await apiCall(`${API_BASE_URL}/files/${filePath}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'text/plain',
|
|
||||||
},
|
|
||||||
body: content,
|
|
||||||
});
|
|
||||||
return response.text();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteFile = async (filePath) => {
|
|
||||||
const response = await apiCall(`${API_BASE_URL}/files/${filePath}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
});
|
|
||||||
return response.text();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchUserSettings = async (userId) => {
|
|
||||||
const response = await apiCall(`${API_BASE_URL}/settings?userId=${userId}`);
|
|
||||||
return response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const saveUserSettings = async (settings) => {
|
|
||||||
const response = await apiCall(`${API_BASE_URL}/settings`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(settings),
|
|
||||||
});
|
|
||||||
return response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const pullChanges = async () => {
|
|
||||||
const response = await apiCall(`${API_BASE_URL}/git/pull`, {
|
|
||||||
method: 'POST',
|
|
||||||
});
|
|
||||||
return response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const commitAndPush = async (message) => {
|
|
||||||
const response = await apiCall(`${API_BASE_URL}/git/commit`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ message }),
|
|
||||||
});
|
|
||||||
return response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getFileUrl = (filePath) => {
|
|
||||||
return `${API_BASE_URL}/files/${filePath}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const lookupFileByName = async (filename) => {
|
|
||||||
const response = await apiCall(
|
const response = await apiCall(
|
||||||
`${API_BASE_URL}/files/lookup?filename=${encodeURIComponent(filename)}`
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/files`
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchFileContent = async (workspaceId, filePath) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/files/${filePath}`
|
||||||
|
);
|
||||||
|
return response.text();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveFileContent = async (workspaceId, filePath, content) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/files/${filePath}`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain',
|
||||||
|
},
|
||||||
|
body: content,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.text();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteFile = async (workspaceId, filePath) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/files/${filePath}`,
|
||||||
|
{
|
||||||
|
method: 'DELETE',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.text();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchWorkspaceSettings = async (workspaceId) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/settings`
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveWorkspaceSettings = async (workspaceId, settings) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/settings`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ settings }),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pullChanges = async (workspaceId) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/git/pull`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const commitAndPush = async (workspaceId, message) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/git/commit`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ message }),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFileUrl = (workspaceId, filePath) => {
|
||||||
|
return `${API_BASE_URL}/users/1/workspaces/${workspaceId}/files/${filePath}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const lookupFileByName = async (workspaceId, filename) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/files/lookup?filename=${encodeURIComponent(
|
||||||
|
filename
|
||||||
|
)}`
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data.paths;
|
return data.paths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateLastOpenedFile = async (workspaceId, filePath) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/files/last`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ filePath }),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLastOpenedFile = async (workspaceId) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}/files/last`
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listWorkspaces = async () => {
|
||||||
|
const response = await apiCall(`${API_BASE_URL}/users/1/workspaces`);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createWorkspace = async (name) => {
|
||||||
|
const response = await apiCall(`${API_BASE_URL}/users/1/workspaces`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ name }),
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateWorkspace = async (workspaceId, name) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ name }),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteWorkspace = async (workspaceId) => {
|
||||||
|
const response = await apiCall(
|
||||||
|
`${API_BASE_URL}/users/1/workspaces/${workspaceId}`,
|
||||||
|
{
|
||||||
|
method: 'DELETE',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateLastWorkspace = async (workspaceId) => {
|
||||||
|
const response = await apiCall(`${API_BASE_URL}/users/1/workspaces/last`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ workspaceId }),
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user