mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 16:04:23 +00:00
Remove more contexts
This commit is contained in:
@@ -4,9 +4,6 @@ import Header from './components/Header';
|
|||||||
import MainContent from './components/MainContent';
|
import MainContent from './components/MainContent';
|
||||||
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
|
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
|
||||||
import { ModalProvider } from './contexts/ModalContext';
|
import { ModalProvider } from './contexts/ModalContext';
|
||||||
import { FileSelectionProvider } from './contexts/FileSelectionContext';
|
|
||||||
import { EditorContentProvider } from './contexts/EditorContentContext';
|
|
||||||
import { FileManagementProvider } from './contexts/FileManagementContext';
|
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
@@ -33,13 +30,7 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<SettingsProvider>
|
<SettingsProvider>
|
||||||
<ModalProvider>
|
<ModalProvider>
|
||||||
<FileManagementProvider>
|
|
||||||
<FileSelectionProvider>
|
|
||||||
<EditorContentProvider>
|
|
||||||
<AppContent />
|
<AppContent />
|
||||||
</EditorContentProvider>
|
|
||||||
</FileSelectionProvider>
|
|
||||||
</FileManagementProvider>
|
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
</SettingsProvider>
|
</SettingsProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ import MarkdownPreview from './MarkdownPreview';
|
|||||||
import { Text } from '@geist-ui/core';
|
import { Text } from '@geist-ui/core';
|
||||||
import { getFileUrl } from '../services/api';
|
import { getFileUrl } from '../services/api';
|
||||||
import { isImageFile } from '../utils/fileHelpers';
|
import { isImageFile } from '../utils/fileHelpers';
|
||||||
import { useFileSelection } from '../contexts/FileSelectionContext';
|
|
||||||
|
|
||||||
const ContentView = ({ activeTab }) => {
|
|
||||||
const { selectedFile } = useFileSelection();
|
|
||||||
|
|
||||||
|
const ContentView = ({
|
||||||
|
activeTab,
|
||||||
|
selectedFile,
|
||||||
|
content,
|
||||||
|
handleContentChange,
|
||||||
|
handleSave,
|
||||||
|
handleLinkClick,
|
||||||
|
}) => {
|
||||||
if (!selectedFile) {
|
if (!selectedFile) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -40,7 +44,16 @@ const ContentView = ({ activeTab }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return activeTab === 'source' ? <Editor /> : <MarkdownPreview />;
|
return activeTab === 'source' ? (
|
||||||
|
<Editor
|
||||||
|
content={content}
|
||||||
|
handleContentChange={handleContentChange}
|
||||||
|
handleSave={handleSave}
|
||||||
|
selectedFile={selectedFile}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MarkdownPreview content={content} handleLinkClick={handleLinkClick} />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ContentView;
|
export default ContentView;
|
||||||
|
|||||||
@@ -6,12 +6,8 @@ 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 { useSettings } from '../contexts/SettingsContext';
|
||||||
import { useFileSelection } from '../contexts/FileSelectionContext';
|
|
||||||
import { useEditorContent } from '../contexts/EditorContentContext';
|
|
||||||
|
|
||||||
const Editor = () => {
|
const Editor = (content, handleContentChange, handleSave, selectedFile) => {
|
||||||
const { content, handleContentChange, handleSave } = useEditorContent();
|
|
||||||
const { selectedFile } = useFileSelection();
|
|
||||||
const { settings } = useSettings();
|
const { settings } = useSettings();
|
||||||
const editorRef = useRef();
|
const editorRef = useRef();
|
||||||
const viewRef = useRef();
|
const viewRef = useRef();
|
||||||
|
|||||||
@@ -2,16 +2,9 @@ import React from 'react';
|
|||||||
import { Button, Tooltip, ButtonGroup, Spacer } from '@geist-ui/core';
|
import { Button, Tooltip, ButtonGroup, Spacer } from '@geist-ui/core';
|
||||||
import { Plus, Trash, GitPullRequest, GitCommit } from '@geist-ui/icons';
|
import { Plus, Trash, GitPullRequest, GitCommit } from '@geist-ui/icons';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useSettings } from '../contexts/SettingsContext';
|
||||||
import { useFileSelection } from '../contexts/FileSelectionContext';
|
|
||||||
import { useModalContext } from '../contexts/ModalContext';
|
import { useModalContext } from '../contexts/ModalContext';
|
||||||
|
|
||||||
const FileActions = ({
|
const FileActions = ({ handlePullChanges, selectedFile }) => {
|
||||||
onCreateFile,
|
|
||||||
onDeleteFile,
|
|
||||||
onPullChanges,
|
|
||||||
onCommitAndPush,
|
|
||||||
}) => {
|
|
||||||
const { selectedFile } = useFileSelection();
|
|
||||||
const { settings } = useSettings();
|
const { settings } = useSettings();
|
||||||
const {
|
const {
|
||||||
setNewFileModalVisible,
|
setNewFileModalVisible,
|
||||||
@@ -62,7 +55,7 @@ const FileActions = ({
|
|||||||
icon={<GitPullRequest />}
|
icon={<GitPullRequest />}
|
||||||
auto
|
auto
|
||||||
scale={2 / 3}
|
scale={2 / 3}
|
||||||
onClick={onPullChanges}
|
onClick={handlePullChanges}
|
||||||
disabled={!settings.gitEnabled}
|
disabled={!settings.gitEnabled}
|
||||||
px={0.6}
|
px={0.6}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ import React from 'react';
|
|||||||
import { Tree } from '@geist-ui/core';
|
import { Tree } from '@geist-ui/core';
|
||||||
import { File, Folder, Image } from '@geist-ui/icons';
|
import { File, Folder, Image } from '@geist-ui/icons';
|
||||||
import { isImageFile } from '../utils/fileHelpers';
|
import { isImageFile } from '../utils/fileHelpers';
|
||||||
import { useFileSelection } from '../contexts/FileSelectionContext';
|
|
||||||
|
|
||||||
const FileTree = ({ files }) => {
|
|
||||||
const { selectedFile, handleFileSelect } = useFileSelection();
|
|
||||||
|
|
||||||
|
const FileTree = ({ files, selectedFile, handleFileSelect }) => {
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return <div>No files to display</div>;
|
return <div>No files to display</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,89 +1,52 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
|
import { Breadcrumbs, Grid, Tabs } from '@geist-ui/core';
|
||||||
|
import { Code, Eye } from '@geist-ui/icons';
|
||||||
|
|
||||||
import FileActions from './FileActions';
|
import FileActions from './FileActions';
|
||||||
import FileTree from './FileTree';
|
import FileTree from './FileTree';
|
||||||
import ContentView from './ContentView';
|
import ContentView from './ContentView';
|
||||||
import CreateFileModal from './modals/CreateFileModal';
|
import CreateFileModal from './modals/CreateFileModal';
|
||||||
import DeleteFileModal from './modals/DeleteFileModal';
|
import DeleteFileModal from './modals/DeleteFileModal';
|
||||||
import CommitMessageModal from './modals/CommitMessageModal';
|
import CommitMessageModal from './modals/CommitMessageModal';
|
||||||
import { useEditorContent } from '../contexts/EditorContentContext';
|
|
||||||
import { useFileSelection } from '../contexts/FileSelectionContext';
|
import { useFileContent } from '../hooks/useFileContent';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useFileList } from '../hooks/useFileList';
|
||||||
import { useFileOperations } from '../hooks/useFileOperations';
|
import { useFileOperations } from '../hooks/useFileOperations';
|
||||||
import { pullChanges, commitAndPush, fetchFileList } from '../services/api';
|
import { useGitOperations } from '../hooks/useGitOperations';
|
||||||
import { Breadcrumbs, Grid, Tabs, useToasts } from '@geist-ui/core';
|
import { useFileNavigation } from '../hooks/useFileNavigation';
|
||||||
import { Code, Eye } from '@geist-ui/icons';
|
|
||||||
import { useState, useCallback, useEffect } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const MainContent = () => {
|
const MainContent = () => {
|
||||||
const [activeTab, setActiveTab] = useState('source');
|
const [activeTab, setActiveTab] = useState('source');
|
||||||
const [files, setFiles] = useState([]);
|
const [files, loadFileList] = useFileList();
|
||||||
const { hasUnsavedChanges } = useEditorContent();
|
const { content, hasUnsavedChanges, handleContentChange } = useFileContent();
|
||||||
const { selectedFile } = useFileSelection();
|
const { handleSave, handleCreate, handleDelete } = useFileOperations();
|
||||||
const { settings } = useSettings();
|
const { handleCommitAndPush, handlePull } = useGitOperations();
|
||||||
const { handleCreate, handleDelete } = useFileOperations();
|
const { handleLinkClick, selectedFile, isNewFile, handleFileSelect } =
|
||||||
const { setToast } = useToasts();
|
useFileNavigation();
|
||||||
|
|
||||||
const refreshFileList = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const fileList = await fetchFileList();
|
|
||||||
setFiles(fileList);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch file list:', error);
|
|
||||||
setToast({ text: 'Failed to refresh file list', type: 'error' });
|
|
||||||
}
|
|
||||||
}, [setToast]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshFileList();
|
loadFileList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleTabChange = (value) => {
|
const handleTabChange = (value) => {
|
||||||
setActiveTab(value);
|
setActiveTab(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const pullLatestChanges = useCallback(async () => {
|
|
||||||
if (!settings.gitEnabled) return;
|
|
||||||
try {
|
|
||||||
await pullChanges();
|
|
||||||
await refreshFileList();
|
|
||||||
setToast({ text: 'Successfully pulled latest changes', type: 'success' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to pull latest changes:', error);
|
|
||||||
setToast({ text: 'Failed to pull latest changes', type: 'error' });
|
|
||||||
}
|
|
||||||
}, [settings.gitEnabled, setToast, refreshFileList]);
|
|
||||||
|
|
||||||
const handleCommitAndPush = useCallback(
|
|
||||||
async (message) => {
|
|
||||||
if (!settings.gitEnabled) return;
|
|
||||||
try {
|
|
||||||
await commitAndPush(message);
|
|
||||||
setToast({
|
|
||||||
text: 'Successfully committed and pushed changes',
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to commit and push changes:', error);
|
|
||||||
setToast({ text: 'Failed to commit and push changes', type: 'error' });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[settings.gitEnabled, setToast]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCreateFile = useCallback(
|
const handleCreateFile = useCallback(
|
||||||
async (fileName) => {
|
async (fileName) => {
|
||||||
await handleCreate(fileName);
|
await handleCreate(fileName);
|
||||||
await refreshFileList();
|
await loadFileList();
|
||||||
},
|
},
|
||||||
[handleCreate, refreshFileList]
|
[handleCreate]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDeleteFile = useCallback(
|
const handleDeleteFile = useCallback(
|
||||||
async (filePath) => {
|
async (filePath) => {
|
||||||
await handleDelete(filePath);
|
await handleDelete(filePath);
|
||||||
await refreshFileList();
|
await loadFileList();
|
||||||
},
|
},
|
||||||
[handleDelete, refreshFileList]
|
[handleDelete]
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBreadcrumbs = () => {
|
const renderBreadcrumbs = () => {
|
||||||
@@ -109,12 +72,14 @@ const MainContent = () => {
|
|||||||
<Grid xs={24} sm={6} md={5} lg={4} height="100%" className="sidebar">
|
<Grid xs={24} sm={6} md={5} lg={4} height="100%" className="sidebar">
|
||||||
<div className="file-tree-container">
|
<div className="file-tree-container">
|
||||||
<FileActions
|
<FileActions
|
||||||
onCreateFile={handleCreateFile}
|
handlePullChanges={handlePull}
|
||||||
onDeleteFile={handleDeleteFile}
|
selectedFile={selectedFile}
|
||||||
onPullChanges={pullLatestChanges}
|
/>
|
||||||
onCommitAndPush={handleCommitAndPush}
|
<FileTree
|
||||||
|
files={files}
|
||||||
|
selectedFile={selectedFile}
|
||||||
|
handleFileSelect={handleFileSelect}
|
||||||
/>
|
/>
|
||||||
<FileTree files={files} />
|
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
@@ -133,12 +98,22 @@ const MainContent = () => {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div className="content-body">
|
<div className="content-body">
|
||||||
<ContentView activeTab={activeTab} />
|
<ContentView
|
||||||
|
activeTab={activeTab}
|
||||||
|
selectedFile={selectedFile}
|
||||||
|
content={content}
|
||||||
|
handleContentChange={handleContentChange}
|
||||||
|
handleSave={handleSave}
|
||||||
|
handleLinkClick={handleLinkClick}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid.Container>
|
</Grid.Container>
|
||||||
<CreateFileModal onCreateFile={handleCreateFile} />
|
<CreateFileModal onCreateFile={handleCreateFile} />
|
||||||
<DeleteFileModal onDeleteFile={handleDeleteFile} />
|
<DeleteFileModal
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
selectedFile={selectedFile}
|
||||||
|
/>
|
||||||
<CommitMessageModal onCommitAndPush={handleCommitAndPush} />
|
<CommitMessageModal onCommitAndPush={handleCommitAndPush} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,13 +5,9 @@ import rehypeKatex from 'rehype-katex';
|
|||||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||||
import 'katex/dist/katex.min.css';
|
import 'katex/dist/katex.min.css';
|
||||||
import { useFileNavigation } from '../hooks/useFileNavigation';
|
|
||||||
import { lookupFileByName } from '../services/api';
|
import { lookupFileByName } from '../services/api';
|
||||||
import { useEditorContent } from '../contexts/EditorContentContext';
|
|
||||||
|
|
||||||
const MarkdownPreview = () => {
|
const MarkdownPreview = (content, handleLinkClick) => {
|
||||||
const { content } = useEditorContent();
|
|
||||||
const { handleLinkClick } = useFileNavigation();
|
|
||||||
const [processedContent, setProcessedContent] = useState(content);
|
const [processedContent, setProcessedContent] = useState(content);
|
||||||
const baseUrl = window.API_BASE_URL;
|
const baseUrl = window.API_BASE_URL;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modal, Text } from '@geist-ui/core';
|
import { Modal, Text } from '@geist-ui/core';
|
||||||
import { useModalContext } from '../../contexts/ModalContext';
|
import { useModalContext } from '../../contexts/ModalContext';
|
||||||
import { useFileSelection } from '../../contexts/FileSelectionContext';
|
|
||||||
|
|
||||||
const DeleteFileModal = ({ onDeleteFile }) => {
|
const DeleteFileModal = ({ onDeleteFile, selectedFile }) => {
|
||||||
const { selectedFile } = useFileSelection();
|
|
||||||
const { deleteFileModalVisible, setDeleteFileModalVisible } =
|
const { deleteFileModalVisible, setDeleteFileModalVisible } =
|
||||||
useModalContext();
|
useModalContext();
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import React, { createContext, useContext, useEffect } from 'react';
|
|
||||||
import { useFileManagementContext } from './FileManagementContext';
|
|
||||||
|
|
||||||
const EditorContentContext = createContext();
|
|
||||||
|
|
||||||
export const EditorContentProvider = ({ children }) => {
|
|
||||||
const { content, handleContentChange, handleSave, selectedFile } =
|
|
||||||
useFileManagementContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EditorContentContext.Provider
|
|
||||||
value={{ content, handleContentChange, handleSave }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</EditorContentContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useEditorContent = () => {
|
|
||||||
const context = useContext(EditorContentContext);
|
|
||||||
if (context === undefined) {
|
|
||||||
throw new Error(
|
|
||||||
'useEditorContent must be used within an EditorContentProvider'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import React, { createContext, useContext, useMemo } from 'react';
|
|
||||||
import { useFileManagement } from '../hooks/useFileManagement';
|
|
||||||
|
|
||||||
const FileManagementContext = createContext();
|
|
||||||
|
|
||||||
export const FileManagementProvider = ({ children }) => {
|
|
||||||
const fileManagement = useFileManagement();
|
|
||||||
|
|
||||||
const value = useMemo(
|
|
||||||
() => fileManagement,
|
|
||||||
[
|
|
||||||
fileManagement.selectedFile,
|
|
||||||
fileManagement.isNewFile,
|
|
||||||
fileManagement.content,
|
|
||||||
fileManagement.isLoading,
|
|
||||||
fileManagement.error,
|
|
||||||
fileManagement.hasUnsavedChanges,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FileManagementContext.Provider value={value}>
|
|
||||||
{children}
|
|
||||||
</FileManagementContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFileManagementContext = () => {
|
|
||||||
const context = useContext(FileManagementContext);
|
|
||||||
if (context === undefined) {
|
|
||||||
throw new Error(
|
|
||||||
'useFileManagementContext must be used within a FileManagementProvider'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React, { createContext, useContext } from 'react';
|
|
||||||
import { useFileManagementContext } from './FileManagementContext';
|
|
||||||
|
|
||||||
const FileSelectionContext = createContext();
|
|
||||||
|
|
||||||
export const FileSelectionProvider = ({ children }) => {
|
|
||||||
const { selectedFile, handleFileSelect } = useFileManagementContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FileSelectionContext.Provider value={{ selectedFile, handleFileSelect }}>
|
|
||||||
{children}
|
|
||||||
</FileSelectionContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFileSelection = () => {
|
|
||||||
const context = useContext(FileSelectionContext);
|
|
||||||
if (context === undefined) {
|
|
||||||
throw new Error(
|
|
||||||
'useFileSelection must be used within a FileSelectionProvider'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
@@ -5,13 +5,9 @@ import { DEFAULT_FILE } from '../utils/constants';
|
|||||||
|
|
||||||
export const useFileContent = () => {
|
export const useFileContent = () => {
|
||||||
const [content, setContent] = useState(DEFAULT_FILE.content);
|
const [content, setContent] = useState(DEFAULT_FILE.content);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||||||
|
|
||||||
const loadFileContent = useCallback(async (filePath) => {
|
const loadFileContent = useCallback(async (filePath) => {
|
||||||
setIsLoading(true);
|
|
||||||
setError(null);
|
|
||||||
try {
|
try {
|
||||||
if (filePath === DEFAULT_FILE.path) {
|
if (filePath === DEFAULT_FILE.path) {
|
||||||
setContent(DEFAULT_FILE.content);
|
setContent(DEFAULT_FILE.content);
|
||||||
@@ -23,10 +19,7 @@ export const useFileContent = () => {
|
|||||||
}
|
}
|
||||||
setHasUnsavedChanges(false);
|
setHasUnsavedChanges(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError('Failed to load file content');
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -38,9 +31,8 @@ export const useFileContent = () => {
|
|||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
setContent,
|
setContent,
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
hasUnsavedChanges,
|
hasUnsavedChanges,
|
||||||
|
setHasUnsavedChanges,
|
||||||
loadFileContent,
|
loadFileContent,
|
||||||
handleContentChange,
|
handleContentChange,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { fetchFileList } from '../services/api';
|
|||||||
|
|
||||||
export const useFileList = (gitEnabled) => {
|
export const useFileList = (gitEnabled) => {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
const [error, setError] = useState(null);
|
|
||||||
|
|
||||||
const loadFileList = useCallback(async () => {
|
const loadFileList = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -15,13 +14,12 @@ export const useFileList = (gitEnabled) => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load file list:', error);
|
console.error('Failed to load file list:', error);
|
||||||
setError('Failed to load file list. Please try again later.');
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadFileList();
|
loadFileList();
|
||||||
}, [loadFileList, gitEnabled]);
|
}, [gitEnabled]);
|
||||||
|
|
||||||
return { files, error, loadFileList };
|
return { files, loadFileList };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import { useEffect, useCallback } from 'react';
|
|
||||||
import { useFileSelection } from './useFileSelection';
|
|
||||||
import { useFileContent } from './useFileContent';
|
|
||||||
|
|
||||||
export const useFileManagement = () => {
|
|
||||||
const { selectedFile, isNewFile, handleFileSelect } = useFileSelection();
|
|
||||||
const {
|
|
||||||
content,
|
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
hasUnsavedChanges,
|
|
||||||
loadFileContent,
|
|
||||||
handleContentChange,
|
|
||||||
} = useFileContent();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedFile) {
|
|
||||||
loadFileContent(selectedFile);
|
|
||||||
}
|
|
||||||
}, [selectedFile, loadFileContent]);
|
|
||||||
|
|
||||||
const handleFileSelectAndLoad = useCallback(
|
|
||||||
async (filePath) => {
|
|
||||||
await handleFileSelect(filePath);
|
|
||||||
await loadFileContent(filePath);
|
|
||||||
},
|
|
||||||
[handleFileSelect, loadFileContent]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
selectedFile,
|
|
||||||
isNewFile,
|
|
||||||
content,
|
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
hasUnsavedChanges,
|
|
||||||
handleFileSelect: handleFileSelectAndLoad,
|
|
||||||
handleContentChange,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,38 +1,39 @@
|
|||||||
// hooks/useFileNavigation.js
|
// hooks/useFileNavigation.js
|
||||||
import { useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { useToasts } from '@geist-ui/core';
|
import { useToasts } from '@geist-ui/core';
|
||||||
import { lookupFileByName } from '../services/api';
|
import { lookupFileByName } from '../services/api';
|
||||||
import { useFileSelection } from '../contexts/FileSelectionContext';
|
import { DEFAULT_FILE } from '../utils/constants'; // Assuming you have this constant defined
|
||||||
|
|
||||||
export const useFileNavigation = () => {
|
export const useFileNavigation = () => {
|
||||||
const { setToast } = useToasts();
|
const { setToast } = useToasts();
|
||||||
const { handleFileSelect } = useFileSelection();
|
|
||||||
|
const [selectedFile, setSelectedFile] = useState(DEFAULT_FILE.path);
|
||||||
|
const [isNewFile, setIsNewFile] = useState(true);
|
||||||
|
|
||||||
const handleLinkClick = useCallback(
|
const handleLinkClick = useCallback(
|
||||||
async (filename) => {
|
async (filename) => {
|
||||||
try {
|
try {
|
||||||
const filePaths = await lookupFileByName(filename);
|
const filePaths = await lookupFileByName(filename);
|
||||||
if (filePaths.length === 1) {
|
if (filePaths.length >= 1) {
|
||||||
handleFileSelect(filePaths[0]);
|
handleFileSelect(filePaths[0]);
|
||||||
} else if (filePaths.length > 1) {
|
|
||||||
// Handle multiple file options (you may want to show a modal or dropdown)
|
|
||||||
setToast({
|
|
||||||
text: 'Multiple files found with the same name. Please specify the full path.',
|
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setToast({ text: `File "${filename}" not found`, type: 'error' });
|
setToast({ text: `File "${filename}" not found`, type: 'error' });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error looking up file:', error);
|
console.error('Error looking up file:', error);
|
||||||
setToast({
|
setToast({
|
||||||
text: 'Failed to lookup file. Please try again.',
|
text: 'Failed to lookup file.',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleFileSelect, setToast]
|
[handleFileSelect]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { handleLinkClick };
|
const handleFileSelect = useCallback(async (filePath) => {
|
||||||
|
setSelectedFile(filePath);
|
||||||
|
setIsNewFile(filePath === DEFAULT_FILE.path);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { handleLinkClick, selectedFile, isNewFile, handleFileSelect };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import { useToasts } from '@geist-ui/core';
|
|||||||
export const useFileOperations = (setHasUnsavedChanges) => {
|
export const useFileOperations = (setHasUnsavedChanges) => {
|
||||||
const { setToast } = useToasts();
|
const { setToast } = useToasts();
|
||||||
|
|
||||||
const handleSave = useCallback(
|
const handleSave = useCallback(async (filePath, content) => {
|
||||||
async (filePath, content) => {
|
|
||||||
try {
|
try {
|
||||||
await saveFileContent(filePath, content);
|
await saveFileContent(filePath, content);
|
||||||
setHasUnsavedChanges(false);
|
setHasUnsavedChanges(false);
|
||||||
@@ -16,9 +15,7 @@ export const useFileOperations = (setHasUnsavedChanges) => {
|
|||||||
console.error('Error saving file:', error);
|
console.error('Error saving file:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
}, []);
|
||||||
[setHasUnsavedChanges]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDelete = useCallback(async (filePath) => {
|
const handleDelete = useCallback(async (filePath) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { useState, useCallback } from 'react';
|
|
||||||
import { DEFAULT_FILE } from '../utils/constants'; // Assuming you have this constant defined
|
|
||||||
|
|
||||||
export const useFileSelection = () => {
|
|
||||||
const [selectedFile, setSelectedFile] = useState(DEFAULT_FILE.path);
|
|
||||||
const [isNewFile, setIsNewFile] = useState(true);
|
|
||||||
|
|
||||||
const handleFileSelect = useCallback(async (filePath) => {
|
|
||||||
setSelectedFile(filePath);
|
|
||||||
setIsNewFile(filePath === DEFAULT_FILE.path);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { selectedFile, isNewFile, handleFileSelect };
|
|
||||||
};
|
|
||||||
@@ -2,13 +2,15 @@ import { useCallback } from 'react';
|
|||||||
import { pullChanges, commitAndPush } from '../services/api';
|
import { pullChanges, commitAndPush } from '../services/api';
|
||||||
|
|
||||||
export const useGitOperations = (gitEnabled) => {
|
export const useGitOperations = (gitEnabled) => {
|
||||||
const pullLatestChanges = useCallback(async () => {
|
const handlePull = useCallback(async () => {
|
||||||
if (!gitEnabled) return false;
|
if (!gitEnabled) return false;
|
||||||
try {
|
try {
|
||||||
await pullChanges();
|
await pullChanges();
|
||||||
|
setToast({ text: 'Successfully pulled latest changes', type: 'success' });
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to pull latest changes:', error);
|
console.error('Failed to pull latest changes:', error);
|
||||||
|
setToast({ text: 'Failed to pull latest changes', type: 'error' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}, [gitEnabled]);
|
}, [gitEnabled]);
|
||||||
@@ -18,14 +20,19 @@ export const useGitOperations = (gitEnabled) => {
|
|||||||
if (!gitEnabled) return false;
|
if (!gitEnabled) return false;
|
||||||
try {
|
try {
|
||||||
await commitAndPush(message);
|
await commitAndPush(message);
|
||||||
|
setToast({
|
||||||
|
text: 'Successfully committed and pushed changes',
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to commit and push changes:', error);
|
console.error('Failed to commit and push changes:', error);
|
||||||
|
setToast({ text: 'Failed to commit and push changes', type: 'error' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[gitEnabled]
|
[gitEnabled]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { pullLatestChanges, handleCommitAndPush };
|
return { handlePull, handleCommitAndPush };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
|
import { IMAGE_EXTENSIONS } from './constants';
|
||||||
|
|
||||||
export const isImageFile = (filePath) => {
|
export const isImageFile = (filePath) => {
|
||||||
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg'];
|
return IMAGE_EXTENSIONS.some((ext) => filePath.toLowerCase().endsWith(ext));
|
||||||
return imageExtensions.some((ext) => filePath.toLowerCase().endsWith(ext));
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user