diff --git a/frontend/src/App.js b/frontend/src/App.js index 231af29..77cd36c 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -4,9 +4,6 @@ import Header from './components/Header'; import MainContent from './components/MainContent'; import { SettingsProvider, useSettings } from './contexts/SettingsContext'; import { ModalProvider } from './contexts/ModalContext'; -import { FileSelectionProvider } from './contexts/FileSelectionContext'; -import { EditorContentProvider } from './contexts/EditorContentContext'; -import { FileManagementProvider } from './contexts/FileManagementContext'; import './App.scss'; function AppContent() { @@ -33,13 +30,7 @@ function App() { return ( - - - - - - - + ); diff --git a/frontend/src/components/ContentView.js b/frontend/src/components/ContentView.js index a9ab3ed..833c74b 100644 --- a/frontend/src/components/ContentView.js +++ b/frontend/src/components/ContentView.js @@ -4,11 +4,15 @@ import MarkdownPreview from './MarkdownPreview'; import { Text } from '@geist-ui/core'; import { getFileUrl } from '../services/api'; 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) { return (
{ ); } - return activeTab === 'source' ? : ; + return activeTab === 'source' ? ( + + ) : ( + + ); }; export default ContentView; diff --git a/frontend/src/components/Editor.js b/frontend/src/components/Editor.js index d9554ca..d71a835 100644 --- a/frontend/src/components/Editor.js +++ b/frontend/src/components/Editor.js @@ -6,12 +6,8 @@ import { markdown } from '@codemirror/lang-markdown'; import { defaultKeymap } from '@codemirror/commands'; import { oneDark } from '@codemirror/theme-one-dark'; import { useSettings } from '../contexts/SettingsContext'; -import { useFileSelection } from '../contexts/FileSelectionContext'; -import { useEditorContent } from '../contexts/EditorContentContext'; -const Editor = () => { - const { content, handleContentChange, handleSave } = useEditorContent(); - const { selectedFile } = useFileSelection(); +const Editor = (content, handleContentChange, handleSave, selectedFile) => { const { settings } = useSettings(); const editorRef = useRef(); const viewRef = useRef(); diff --git a/frontend/src/components/FileActions.js b/frontend/src/components/FileActions.js index 70ffda3..78eaf4b 100644 --- a/frontend/src/components/FileActions.js +++ b/frontend/src/components/FileActions.js @@ -2,16 +2,9 @@ import React from 'react'; import { Button, Tooltip, ButtonGroup, Spacer } from '@geist-ui/core'; import { Plus, Trash, GitPullRequest, GitCommit } from '@geist-ui/icons'; import { useSettings } from '../contexts/SettingsContext'; -import { useFileSelection } from '../contexts/FileSelectionContext'; import { useModalContext } from '../contexts/ModalContext'; -const FileActions = ({ - onCreateFile, - onDeleteFile, - onPullChanges, - onCommitAndPush, -}) => { - const { selectedFile } = useFileSelection(); +const FileActions = ({ handlePullChanges, selectedFile }) => { const { settings } = useSettings(); const { setNewFileModalVisible, @@ -62,7 +55,7 @@ const FileActions = ({ icon={} auto scale={2 / 3} - onClick={onPullChanges} + onClick={handlePullChanges} disabled={!settings.gitEnabled} px={0.6} /> diff --git a/frontend/src/components/FileTree.js b/frontend/src/components/FileTree.js index ac8dd85..5f15573 100644 --- a/frontend/src/components/FileTree.js +++ b/frontend/src/components/FileTree.js @@ -2,11 +2,8 @@ import React from 'react'; import { Tree } from '@geist-ui/core'; import { File, Folder, Image } from '@geist-ui/icons'; 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) { return
No files to display
; } diff --git a/frontend/src/components/MainContent.js b/frontend/src/components/MainContent.js index f84306e..5173d33 100644 --- a/frontend/src/components/MainContent.js +++ b/frontend/src/components/MainContent.js @@ -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 FileTree from './FileTree'; import ContentView from './ContentView'; import CreateFileModal from './modals/CreateFileModal'; import DeleteFileModal from './modals/DeleteFileModal'; import CommitMessageModal from './modals/CommitMessageModal'; -import { useEditorContent } from '../contexts/EditorContentContext'; -import { useFileSelection } from '../contexts/FileSelectionContext'; -import { useSettings } from '../contexts/SettingsContext'; + +import { useFileContent } from '../hooks/useFileContent'; +import { useFileList } from '../hooks/useFileList'; import { useFileOperations } from '../hooks/useFileOperations'; -import { pullChanges, commitAndPush, fetchFileList } from '../services/api'; -import { Breadcrumbs, Grid, Tabs, useToasts } from '@geist-ui/core'; -import { Code, Eye } from '@geist-ui/icons'; -import { useState, useCallback, useEffect } from 'react'; -import React from 'react'; +import { useGitOperations } from '../hooks/useGitOperations'; +import { useFileNavigation } from '../hooks/useFileNavigation'; const MainContent = () => { const [activeTab, setActiveTab] = useState('source'); - const [files, setFiles] = useState([]); - const { hasUnsavedChanges } = useEditorContent(); - const { selectedFile } = useFileSelection(); - const { settings } = useSettings(); - const { handleCreate, handleDelete } = useFileOperations(); - const { setToast } = useToasts(); - - 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]); + const [files, loadFileList] = useFileList(); + const { content, hasUnsavedChanges, handleContentChange } = useFileContent(); + const { handleSave, handleCreate, handleDelete } = useFileOperations(); + const { handleCommitAndPush, handlePull } = useGitOperations(); + const { handleLinkClick, selectedFile, isNewFile, handleFileSelect } = + useFileNavigation(); useEffect(() => { - refreshFileList(); + loadFileList(); }, []); const handleTabChange = (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( async (fileName) => { await handleCreate(fileName); - await refreshFileList(); + await loadFileList(); }, - [handleCreate, refreshFileList] + [handleCreate] ); const handleDeleteFile = useCallback( async (filePath) => { await handleDelete(filePath); - await refreshFileList(); + await loadFileList(); }, - [handleDelete, refreshFileList] + [handleDelete] ); const renderBreadcrumbs = () => { @@ -109,12 +72,14 @@ const MainContent = () => {
+ -
{
- +
- + ); diff --git a/frontend/src/components/MarkdownPreview.js b/frontend/src/components/MarkdownPreview.js index 2834476..aa31aa8 100644 --- a/frontend/src/components/MarkdownPreview.js +++ b/frontend/src/components/MarkdownPreview.js @@ -5,13 +5,9 @@ import rehypeKatex from 'rehype-katex'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import 'katex/dist/katex.min.css'; -import { useFileNavigation } from '../hooks/useFileNavigation'; import { lookupFileByName } from '../services/api'; -import { useEditorContent } from '../contexts/EditorContentContext'; -const MarkdownPreview = () => { - const { content } = useEditorContent(); - const { handleLinkClick } = useFileNavigation(); +const MarkdownPreview = (content, handleLinkClick) => { const [processedContent, setProcessedContent] = useState(content); const baseUrl = window.API_BASE_URL; diff --git a/frontend/src/components/modals/DeleteFileModal.js b/frontend/src/components/modals/DeleteFileModal.js index 50714a6..c267466 100644 --- a/frontend/src/components/modals/DeleteFileModal.js +++ b/frontend/src/components/modals/DeleteFileModal.js @@ -1,10 +1,8 @@ import React from 'react'; import { Modal, Text } from '@geist-ui/core'; import { useModalContext } from '../../contexts/ModalContext'; -import { useFileSelection } from '../../contexts/FileSelectionContext'; -const DeleteFileModal = ({ onDeleteFile }) => { - const { selectedFile } = useFileSelection(); +const DeleteFileModal = ({ onDeleteFile, selectedFile }) => { const { deleteFileModalVisible, setDeleteFileModalVisible } = useModalContext(); diff --git a/frontend/src/contexts/EditorContentContext.js b/frontend/src/contexts/EditorContentContext.js deleted file mode 100644 index b77128c..0000000 --- a/frontend/src/contexts/EditorContentContext.js +++ /dev/null @@ -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 ( - - {children} - - ); -}; - -export const useEditorContent = () => { - const context = useContext(EditorContentContext); - if (context === undefined) { - throw new Error( - 'useEditorContent must be used within an EditorContentProvider' - ); - } - return context; -}; diff --git a/frontend/src/contexts/FileManagementContext.js b/frontend/src/contexts/FileManagementContext.js deleted file mode 100644 index 2a6e89b..0000000 --- a/frontend/src/contexts/FileManagementContext.js +++ /dev/null @@ -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 ( - - {children} - - ); -}; - -export const useFileManagementContext = () => { - const context = useContext(FileManagementContext); - if (context === undefined) { - throw new Error( - 'useFileManagementContext must be used within a FileManagementProvider' - ); - } - return context; -}; diff --git a/frontend/src/contexts/FileSelectionContext.js b/frontend/src/contexts/FileSelectionContext.js deleted file mode 100644 index b3207f8..0000000 --- a/frontend/src/contexts/FileSelectionContext.js +++ /dev/null @@ -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 ( - - {children} - - ); -}; - -export const useFileSelection = () => { - const context = useContext(FileSelectionContext); - if (context === undefined) { - throw new Error( - 'useFileSelection must be used within a FileSelectionProvider' - ); - } - return context; -}; diff --git a/frontend/src/hooks/useFileContent.js b/frontend/src/hooks/useFileContent.js index 7679f60..53565a0 100644 --- a/frontend/src/hooks/useFileContent.js +++ b/frontend/src/hooks/useFileContent.js @@ -5,13 +5,9 @@ import { DEFAULT_FILE } from '../utils/constants'; export const useFileContent = () => { const [content, setContent] = useState(DEFAULT_FILE.content); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const loadFileContent = useCallback(async (filePath) => { - setIsLoading(true); - setError(null); try { if (filePath === DEFAULT_FILE.path) { setContent(DEFAULT_FILE.content); @@ -23,10 +19,7 @@ export const useFileContent = () => { } setHasUnsavedChanges(false); } catch (err) { - setError('Failed to load file content'); console.error(err); - } finally { - setIsLoading(false); } }, []); @@ -38,9 +31,8 @@ export const useFileContent = () => { return { content, setContent, - isLoading, - error, hasUnsavedChanges, + setHasUnsavedChanges, loadFileContent, handleContentChange, }; diff --git a/frontend/src/hooks/useFileList.js b/frontend/src/hooks/useFileList.js index 4737cf9..0a37baf 100644 --- a/frontend/src/hooks/useFileList.js +++ b/frontend/src/hooks/useFileList.js @@ -3,7 +3,6 @@ import { fetchFileList } from '../services/api'; export const useFileList = (gitEnabled) => { const [files, setFiles] = useState([]); - const [error, setError] = useState(null); const loadFileList = useCallback(async () => { try { @@ -15,13 +14,12 @@ export const useFileList = (gitEnabled) => { } } catch (error) { console.error('Failed to load file list:', error); - setError('Failed to load file list. Please try again later.'); } }, []); useEffect(() => { loadFileList(); - }, [loadFileList, gitEnabled]); + }, [gitEnabled]); - return { files, error, loadFileList }; + return { files, loadFileList }; }; diff --git a/frontend/src/hooks/useFileManagement.js b/frontend/src/hooks/useFileManagement.js deleted file mode 100644 index 7275838..0000000 --- a/frontend/src/hooks/useFileManagement.js +++ /dev/null @@ -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, - }; -}; diff --git a/frontend/src/hooks/useFileNavigation.js b/frontend/src/hooks/useFileNavigation.js index f5f21ef..5951272 100644 --- a/frontend/src/hooks/useFileNavigation.js +++ b/frontend/src/hooks/useFileNavigation.js @@ -1,38 +1,39 @@ // hooks/useFileNavigation.js -import { useCallback } from 'react'; +import { useState, useCallback } from 'react'; import { useToasts } from '@geist-ui/core'; 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 = () => { const { setToast } = useToasts(); - const { handleFileSelect } = useFileSelection(); + + const [selectedFile, setSelectedFile] = useState(DEFAULT_FILE.path); + const [isNewFile, setIsNewFile] = useState(true); const handleLinkClick = useCallback( async (filename) => { try { const filePaths = await lookupFileByName(filename); - if (filePaths.length === 1) { + if (filePaths.length >= 1) { 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 { setToast({ text: `File "${filename}" not found`, type: 'error' }); } } catch (error) { console.error('Error looking up file:', error); setToast({ - text: 'Failed to lookup file. Please try again.', + text: 'Failed to lookup file.', type: 'error', }); } }, - [handleFileSelect, setToast] + [handleFileSelect] ); - return { handleLinkClick }; + const handleFileSelect = useCallback(async (filePath) => { + setSelectedFile(filePath); + setIsNewFile(filePath === DEFAULT_FILE.path); + }, []); + + return { handleLinkClick, selectedFile, isNewFile, handleFileSelect }; }; diff --git a/frontend/src/hooks/useFileOperations.js b/frontend/src/hooks/useFileOperations.js index 4fb0757..f604dde 100644 --- a/frontend/src/hooks/useFileOperations.js +++ b/frontend/src/hooks/useFileOperations.js @@ -5,20 +5,17 @@ import { useToasts } from '@geist-ui/core'; export const useFileOperations = (setHasUnsavedChanges) => { const { setToast } = useToasts(); - const handleSave = useCallback( - async (filePath, content) => { - try { - await saveFileContent(filePath, content); - setHasUnsavedChanges(false); - setToast({ text: 'File saved successfully', type: 'success' }); - return true; - } catch (error) { - console.error('Error saving file:', error); - return false; - } - }, - [setHasUnsavedChanges] - ); + const handleSave = useCallback(async (filePath, content) => { + try { + await saveFileContent(filePath, content); + setHasUnsavedChanges(false); + setToast({ text: 'File saved successfully', type: 'success' }); + return true; + } catch (error) { + console.error('Error saving file:', error); + return false; + } + }, []); const handleDelete = useCallback(async (filePath) => { try { diff --git a/frontend/src/hooks/useFileSelection.js b/frontend/src/hooks/useFileSelection.js deleted file mode 100644 index 8b23ac5..0000000 --- a/frontend/src/hooks/useFileSelection.js +++ /dev/null @@ -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 }; -}; diff --git a/frontend/src/hooks/useGitOperations.js b/frontend/src/hooks/useGitOperations.js index 843c41b..561b9b1 100644 --- a/frontend/src/hooks/useGitOperations.js +++ b/frontend/src/hooks/useGitOperations.js @@ -2,13 +2,15 @@ import { useCallback } from 'react'; import { pullChanges, commitAndPush } from '../services/api'; export const useGitOperations = (gitEnabled) => { - const pullLatestChanges = useCallback(async () => { + const handlePull = useCallback(async () => { if (!gitEnabled) return false; try { await pullChanges(); + setToast({ text: 'Successfully pulled latest changes', type: 'success' }); return true; } catch (error) { console.error('Failed to pull latest changes:', error); + setToast({ text: 'Failed to pull latest changes', type: 'error' }); return false; } }, [gitEnabled]); @@ -18,14 +20,19 @@ export const useGitOperations = (gitEnabled) => { if (!gitEnabled) return false; try { await commitAndPush(message); + setToast({ + text: 'Successfully committed and pushed changes', + type: 'success', + }); return true; } catch (error) { console.error('Failed to commit and push changes:', error); + setToast({ text: 'Failed to commit and push changes', type: 'error' }); return false; } }, [gitEnabled] ); - return { pullLatestChanges, handleCommitAndPush }; + return { handlePull, handleCommitAndPush }; }; diff --git a/frontend/src/utils/fileHelpers.js b/frontend/src/utils/fileHelpers.js index f2e95a5..7d0c1a0 100644 --- a/frontend/src/utils/fileHelpers.js +++ b/frontend/src/utils/fileHelpers.js @@ -1,4 +1,5 @@ +import { IMAGE_EXTENSIONS } from './constants'; + export const isImageFile = (filePath) => { - const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg']; - return imageExtensions.some((ext) => filePath.toLowerCase().endsWith(ext)); + return IMAGE_EXTENSIONS.some((ext) => filePath.toLowerCase().endsWith(ext)); };