From 50b0fbb03c5a7652e2670d5143a5ae8e246d507d Mon Sep 17 00:00:00 2001 From: LordMathis Date: Fri, 4 Oct 2024 23:09:40 +0200 Subject: [PATCH] Split large contexts --- frontend/src/App.js | 29 +++++++++------ frontend/src/components/ContentView.js | 8 ++--- frontend/src/components/Editor.js | 7 ++-- frontend/src/components/FileActions.js | 8 ++--- frontend/src/components/FileTree.js | 4 +-- frontend/src/components/Header.js | 4 +-- frontend/src/components/MainContent.js | 10 +++--- frontend/src/components/MarkdownPreview.js | 4 +-- frontend/src/components/Settings.js | 6 ++-- .../components/modals/CommitMessageModal.js | 4 +-- .../src/components/modals/CreateFileModal.js | 8 ++--- .../src/components/modals/DeleteFileModal.js | 8 ++--- frontend/src/contexts/EditorContentContext.js | 33 +++++++++++++++++ frontend/src/contexts/FileContentContext.js | 24 ------------- frontend/src/contexts/FileListContext.js | 10 +++--- .../src/contexts/FileOperationsContext.js | 32 +++++++++++++++++ frontend/src/contexts/FileSelectionContext.js | 35 +++++++++++++++++++ .../{UIStateContext.js => ModalContext.js} | 17 +++------ frontend/src/contexts/TabContext.js | 15 ++++++++ frontend/src/hooks/useFileNavigation.js | 4 +-- 20 files changed, 183 insertions(+), 87 deletions(-) create mode 100644 frontend/src/contexts/EditorContentContext.js delete mode 100644 frontend/src/contexts/FileContentContext.js create mode 100644 frontend/src/contexts/FileOperationsContext.js create mode 100644 frontend/src/contexts/FileSelectionContext.js rename frontend/src/contexts/{UIStateContext.js => ModalContext.js} (58%) create mode 100644 frontend/src/contexts/TabContext.js diff --git a/frontend/src/App.js b/frontend/src/App.js index 204bf1e..6d8e561 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -4,10 +4,13 @@ import { GeistProvider, CssBaseline, Page } from '@geist-ui/core'; import Header from './components/Header'; import MainContent from './components/MainContent'; import { SettingsProvider, useSettings } from './contexts/SettingsContext'; -import { FileContentProvider } from './contexts/FileContentContext'; -import { FileListProvider } from './contexts/FileListContext'; +import { ModalProvider } from './contexts/ModalContext'; +import { TabProvider } from './contexts/TabContext'; import { GitOperationsProvider } from './contexts/GitOperationsContext'; -import { UIStateProvider } from './contexts/UIStateContext'; +import { FileListProvider } from './contexts/FileListContext'; +import { FileSelectionProvider } from './contexts/FileSelectionContext'; +import { FileOperationsProvider } from './contexts/FileOperationsContext'; +import { EditorContentProvider } from './contexts/EditorContentContext'; import './App.scss'; function AppContent() { @@ -33,15 +36,21 @@ function AppContent() { function App() { return ( - - + + - - - + + + + + + + + + - - + + ); } diff --git a/frontend/src/components/ContentView.js b/frontend/src/components/ContentView.js index a6c04ce..cb88264 100644 --- a/frontend/src/components/ContentView.js +++ b/frontend/src/components/ContentView.js @@ -4,12 +4,12 @@ import MarkdownPreview from './MarkdownPreview'; import { Text } from '@geist-ui/core'; import { getFileUrl } from '../services/api'; import { isImageFile } from '../utils/fileHelpers'; -import { useFileContentContext } from '../contexts/FileContentContext'; -import { useUIStateContext } from '../contexts/UIStateContext'; +import { useFileSelection } from '../contexts/FileSelectionContext'; +import { useTabContext } from '../contexts/TabContext'; const ContentView = () => { - const { selectedFile } = useFileContentContext(); - const { activeTab } = useUIStateContext(); + const { selectedFile } = useFileSelection(); + const { activeTab } = useTabContext(); if (!selectedFile) { return ( diff --git a/frontend/src/components/Editor.js b/frontend/src/components/Editor.js index 77b316c..d9554ca 100644 --- a/frontend/src/components/Editor.js +++ b/frontend/src/components/Editor.js @@ -5,12 +5,13 @@ 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 { useFileContentContext } from '../contexts/FileContentContext'; import { useSettings } from '../contexts/SettingsContext'; +import { useFileSelection } from '../contexts/FileSelectionContext'; +import { useEditorContent } from '../contexts/EditorContentContext'; const Editor = () => { - const { content, selectedFile, handleContentChange, handleSave } = - useFileContentContext(); + const { content, handleContentChange, handleSave } = useEditorContent(); + const { selectedFile } = useFileSelection(); const { settings } = useSettings(); const editorRef = useRef(); const viewRef = useRef(); diff --git a/frontend/src/components/FileActions.js b/frontend/src/components/FileActions.js index e62b58e..24e106d 100644 --- a/frontend/src/components/FileActions.js +++ b/frontend/src/components/FileActions.js @@ -1,20 +1,20 @@ import React from 'react'; import { Button, Tooltip, ButtonGroup, Spacer } from '@geist-ui/core'; import { Plus, Trash, GitPullRequest, GitCommit } from '@geist-ui/icons'; -import { useFileContentContext } from '../contexts/FileContentContext'; import { useGitOperationsContext } from '../contexts/GitOperationsContext'; import { useSettings } from '../contexts/SettingsContext'; -import { useUIStateContext } from '../contexts/UIStateContext'; +import { useFileSelection } from '../contexts/FileSelectionContext'; +import { useModalContext } from '../contexts/ModalContext'; const FileActions = () => { - const { selectedFile } = useFileContentContext(); + const { selectedFile } = useFileSelection(); const { pullLatestChanges } = useGitOperationsContext(); const { settings } = useSettings(); const { setNewFileModalVisible, setDeleteFileModalVisible, setCommitMessageModalVisible, - } = useUIStateContext(); + } = useModalContext(); const handleCreateFile = () => setNewFileModalVisible(true); const handleDeleteFile = () => setDeleteFileModalVisible(true); diff --git a/frontend/src/components/FileTree.js b/frontend/src/components/FileTree.js index 4ff01a4..844b560 100644 --- a/frontend/src/components/FileTree.js +++ b/frontend/src/components/FileTree.js @@ -1,13 +1,13 @@ import React from 'react'; import { Tree } from '@geist-ui/core'; import { File, Folder, Image } from '@geist-ui/icons'; -import { useFileContentContext } from '../contexts/FileContentContext'; import { useFileListContext } from '../contexts/FileListContext'; import { isImageFile } from '../utils/fileHelpers'; +import { useFileSelection } from '../contexts/FileSelectionContext'; const FileTree = () => { const { files } = useFileListContext(); - const { selectedFile, handleFileSelect } = useFileContentContext(); + const { selectedFile, handleFileSelect } = useFileSelection(); if (files.length === 0) { return
No files to display
; diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index 2b6b69d..1406805 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -2,10 +2,10 @@ import React from 'react'; import { Page, Text, User, Button, Spacer } from '@geist-ui/core'; import { Settings as SettingsIcon } from '@geist-ui/icons'; import Settings from './Settings'; -import { useUIStateContext } from '../contexts/UIStateContext'; +import { useModalContext } from '../contexts/ModalContext'; const Header = () => { - const { setSettingsModalVisible } = useUIStateContext(); + const { setSettingsModalVisible } = useModalContext(); const openSettings = () => setSettingsModalVisible(true); diff --git a/frontend/src/components/MainContent.js b/frontend/src/components/MainContent.js index 4f51212..e837b3f 100644 --- a/frontend/src/components/MainContent.js +++ b/frontend/src/components/MainContent.js @@ -7,12 +7,14 @@ import ContentView from './ContentView'; import CreateFileModal from './modals/CreateFileModal'; import DeleteFileModal from './modals/DeleteFileModal'; import CommitMessageModal from './modals/CommitMessageModal'; -import { useFileContentContext } from '../contexts/FileContentContext'; -import { useUIStateContext } from '../contexts/UIStateContext'; +import { useTabContext } from '../contexts/TabContext'; +import { useEditorContent } from '../contexts/EditorContentContext'; +import { useFileSelection } from '../contexts/FileSelectionContext'; const MainContent = () => { - const { selectedFile, hasUnsavedChanges } = useFileContentContext(); - const { activeTab, setActiveTab } = useUIStateContext(); + const { hasUnsavedChanges } = useEditorContent(); + const { selectedFile } = useFileSelection(); + const { activeTab, setActiveTab } = useTabContext(); const handleTabChange = (value) => { setActiveTab(value); diff --git a/frontend/src/components/MarkdownPreview.js b/frontend/src/components/MarkdownPreview.js index 43c89d5..2834476 100644 --- a/frontend/src/components/MarkdownPreview.js +++ b/frontend/src/components/MarkdownPreview.js @@ -5,12 +5,12 @@ 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 { useFileContentContext } from '../contexts/FileContentContext'; import { useFileNavigation } from '../hooks/useFileNavigation'; import { lookupFileByName } from '../services/api'; +import { useEditorContent } from '../contexts/EditorContentContext'; const MarkdownPreview = () => { - const { content } = useFileContentContext(); + const { content } = useEditorContent(); const { handleLinkClick } = useFileNavigation(); const [processedContent, setProcessedContent] = useState(content); const baseUrl = window.API_BASE_URL; diff --git a/frontend/src/components/Settings.js b/frontend/src/components/Settings.js index b79b3f1..811cc69 100644 --- a/frontend/src/components/Settings.js +++ b/frontend/src/components/Settings.js @@ -1,10 +1,10 @@ import React, { useReducer, useEffect, useCallback, useRef } from 'react'; -import { Modal, Spacer, useTheme, Dot, useToasts } from '@geist-ui/core'; +import { Modal, Spacer, Dot, useToasts } from '@geist-ui/core'; import { useSettings } from '../contexts/SettingsContext'; -import { useUIStateContext } from '../contexts/UIStateContext'; import AppearanceSettings from './settings/AppearanceSettings'; import EditorSettings from './settings/EditorSettings'; import GitSettings from './settings/GitSettings'; +import { useModalContext } from '../contexts/ModalContext'; const initialState = { localSettings: {}, @@ -50,7 +50,7 @@ function settingsReducer(state, action) { const Settings = () => { const { settings, updateSettings, updateTheme } = useSettings(); - const { settingsModalVisible, setSettingsModalVisible } = useUIStateContext(); + const { settingsModalVisible, setSettingsModalVisible } = useModalContext(); const { setToast } = useToasts(); const [state, dispatch] = useReducer(settingsReducer, initialState); const isInitialMount = useRef(true); diff --git a/frontend/src/components/modals/CommitMessageModal.js b/frontend/src/components/modals/CommitMessageModal.js index fb66b33..faf3cb8 100644 --- a/frontend/src/components/modals/CommitMessageModal.js +++ b/frontend/src/components/modals/CommitMessageModal.js @@ -1,13 +1,13 @@ import React, { useState } from 'react'; import { Modal, Input } from '@geist-ui/core'; import { useGitOperationsContext } from '../../contexts/GitOperationsContext'; -import { useUIStateContext } from '../../contexts/UIStateContext'; +import { useModalContext } from '../../contexts/ModalContext'; const CommitMessageModal = () => { const [message, setMessage] = useState(''); const { handleCommitAndPush } = useGitOperationsContext(); const { commitMessageModalVisible, setCommitMessageModalVisible } = - useUIStateContext(); + useModalContext(); const handleSubmit = async () => { if (message) { diff --git a/frontend/src/components/modals/CreateFileModal.js b/frontend/src/components/modals/CreateFileModal.js index 06b6173..ea4b57a 100644 --- a/frontend/src/components/modals/CreateFileModal.js +++ b/frontend/src/components/modals/CreateFileModal.js @@ -1,12 +1,12 @@ import React, { useState } from 'react'; import { Modal, Input } from '@geist-ui/core'; -import { useFileContentContext } from '../../contexts/FileContentContext'; -import { useUIStateContext } from '../../contexts/UIStateContext'; +import { useFileOperations } from '../../contexts/FileOperationsContext'; +import { useModalContext } from '../../contexts/ModalContext'; const CreateFileModal = () => { const [fileName, setFileName] = useState(''); - const { newFileModalVisible, setNewFileModalVisible } = useUIStateContext(); - const { handleCreateNewFile } = useFileContentContext(); + const { newFileModalVisible, setNewFileModalVisible } = useModalContext(); + const { handleCreateNewFile } = useFileOperations(); const handleSubmit = async () => { if (fileName) { diff --git a/frontend/src/components/modals/DeleteFileModal.js b/frontend/src/components/modals/DeleteFileModal.js index 600e9be..9abf3dd 100644 --- a/frontend/src/components/modals/DeleteFileModal.js +++ b/frontend/src/components/modals/DeleteFileModal.js @@ -1,12 +1,12 @@ import React from 'react'; import { Modal, Text } from '@geist-ui/core'; -import { useFileContentContext } from '../../contexts/FileContentContext'; -import { useUIStateContext } from '../../contexts/UIStateContext'; +import { useModalContext } from '../../contexts/ModalContext'; +import { useFileSelection } from '../../contexts/FileSelectionContext'; const DeleteFileModal = () => { - const { selectedFile, handleDeleteFile } = useFileContentContext(); + const { selectedFile, handleDeleteFile } = useFileSelection(); const { deleteFileModalVisible, setDeleteFileModalVisible } = - useUIStateContext(); + useModalContext(); const handleConfirm = async () => { await handleDeleteFile(); diff --git a/frontend/src/contexts/EditorContentContext.js b/frontend/src/contexts/EditorContentContext.js new file mode 100644 index 0000000..4bfdd00 --- /dev/null +++ b/frontend/src/contexts/EditorContentContext.js @@ -0,0 +1,33 @@ +import React, { createContext, useContext, useMemo } from 'react'; +import { useFileContent } from '../hooks/useFileContent'; + +const EditorContentContext = createContext(); + +export const EditorContentProvider = ({ children }) => { + const { content, handleContentChange, handleSave } = useFileContent(); + + const value = useMemo( + () => ({ + content, + handleContentChange, + handleSave, + }), + [content, handleContentChange, handleSave] + ); + + 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/FileContentContext.js b/frontend/src/contexts/FileContentContext.js deleted file mode 100644 index b97a7f6..0000000 --- a/frontend/src/contexts/FileContentContext.js +++ /dev/null @@ -1,24 +0,0 @@ -import React, { createContext, useContext } from 'react'; -import { useFileContent } from '../hooks/useFileContent'; - -const FileContentContext = createContext(); - -export const FileContentProvider = ({ children }) => { - const fileContentHook = useFileContent(); - - return ( - - {children} - - ); -}; - -export const useFileContentContext = () => { - const context = useContext(FileContentContext); - if (!context) { - throw new Error( - 'useFileContentContext must be used within a FileContentProvider' - ); - } - return context; -}; diff --git a/frontend/src/contexts/FileListContext.js b/frontend/src/contexts/FileListContext.js index 60c37f7..9192b01 100644 --- a/frontend/src/contexts/FileListContext.js +++ b/frontend/src/contexts/FileListContext.js @@ -1,13 +1,15 @@ -import React, { createContext, useContext } from 'react'; +import React, { createContext, useContext, useMemo } from 'react'; import { useFileList } from '../hooks/useFileList'; const FileListContext = createContext(); export const FileListProvider = ({ children }) => { - const fileListHook = useFileList(); + const { files, loadFileList } = useFileList(); + + const value = useMemo(() => ({ files, loadFileList }), [files, loadFileList]); return ( - + {children} ); @@ -15,7 +17,7 @@ export const FileListProvider = ({ children }) => { export const useFileListContext = () => { const context = useContext(FileListContext); - if (!context) { + if (context === undefined) { throw new Error( 'useFileListContext must be used within a FileListProvider' ); diff --git a/frontend/src/contexts/FileOperationsContext.js b/frontend/src/contexts/FileOperationsContext.js new file mode 100644 index 0000000..41b646b --- /dev/null +++ b/frontend/src/contexts/FileOperationsContext.js @@ -0,0 +1,32 @@ +import React, { createContext, useContext, useMemo } from 'react'; +import { useFileContent } from '../hooks/useFileContent'; + +const FileOperationsContext = createContext(); + +export const FileOperationsProvider = ({ children }) => { + const { handleCreateNewFile, handleDeleteFile } = useFileContent(); + + const value = useMemo( + () => ({ + handleCreateNewFile, + handleDeleteFile, + }), + [handleCreateNewFile, handleDeleteFile] + ); + + return ( + + {children} + + ); +}; + +export const useFileOperations = () => { + const context = useContext(FileOperationsContext); + if (context === undefined) { + throw new Error( + 'useFileOperations must be used within a FileOperationsProvider' + ); + } + return context; +}; diff --git a/frontend/src/contexts/FileSelectionContext.js b/frontend/src/contexts/FileSelectionContext.js new file mode 100644 index 0000000..d89adeb --- /dev/null +++ b/frontend/src/contexts/FileSelectionContext.js @@ -0,0 +1,35 @@ +import React, { createContext, useContext, useMemo } from 'react'; +import { useFileContent } from '../hooks/useFileContent'; + +const FileSelectionContext = createContext(); + +export const FileSelectionProvider = ({ children }) => { + const { selectedFile, isNewFile, hasUnsavedChanges, handleFileSelect } = + useFileContent(); + + const value = useMemo( + () => ({ + selectedFile, + isNewFile, + hasUnsavedChanges, + handleFileSelect, + }), + [selectedFile, isNewFile, hasUnsavedChanges, handleFileSelect] + ); + + 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/contexts/UIStateContext.js b/frontend/src/contexts/ModalContext.js similarity index 58% rename from frontend/src/contexts/UIStateContext.js rename to frontend/src/contexts/ModalContext.js index e36d999..697bdc7 100644 --- a/frontend/src/contexts/UIStateContext.js +++ b/frontend/src/contexts/ModalContext.js @@ -1,9 +1,8 @@ import React, { createContext, useContext, useState } from 'react'; -const UIStateContext = createContext(); +const ModalContext = createContext(); -export const UIStateProvider = ({ children }) => { - const [activeTab, setActiveTab] = useState('source'); +export const ModalProvider = ({ children }) => { const [newFileModalVisible, setNewFileModalVisible] = useState(false); const [deleteFileModalVisible, setDeleteFileModalVisible] = useState(false); const [commitMessageModalVisible, setCommitMessageModalVisible] = @@ -11,8 +10,6 @@ export const UIStateProvider = ({ children }) => { const [settingsModalVisible, setSettingsModalVisible] = useState(false); const value = { - activeTab, - setActiveTab, newFileModalVisible, setNewFileModalVisible, deleteFileModalVisible, @@ -24,14 +21,8 @@ export const UIStateProvider = ({ children }) => { }; return ( - {children} + {children} ); }; -export const useUIStateContext = () => { - const context = useContext(UIStateContext); - if (!context) { - throw new Error('useUIStateContext must be used within a UIStateProvider'); - } - return context; -}; +export const useModalContext = () => useContext(ModalContext); diff --git a/frontend/src/contexts/TabContext.js b/frontend/src/contexts/TabContext.js new file mode 100644 index 0000000..4123479 --- /dev/null +++ b/frontend/src/contexts/TabContext.js @@ -0,0 +1,15 @@ +import React, { createContext, useContext, useState } from 'react'; + +const TabContext = createContext(); + +export const TabProvider = ({ children }) => { + const [activeTab, setActiveTab] = useState('source'); + + return ( + + {children} + + ); +}; + +export const useTabContext = () => useContext(TabContext); diff --git a/frontend/src/hooks/useFileNavigation.js b/frontend/src/hooks/useFileNavigation.js index eb8c46b..7d5c239 100644 --- a/frontend/src/hooks/useFileNavigation.js +++ b/frontend/src/hooks/useFileNavigation.js @@ -2,11 +2,11 @@ import { useCallback } from 'react'; import { useToasts } from '@geist-ui/core'; import { lookupFileByName } from '../services/api'; -import { useFileContentContext } from '../contexts/FileContentContext'; +import { useFileSelection } from '../contexts/FileSelectionContext'; export const useFileNavigation = () => { const { setToast } = useToasts(); - const { handleFileSelect } = useFileContentContext(); + const { handleFileSelect } = useFileSelection(); const handleLinkClick = useCallback( async (filename) => {