Split large components

This commit is contained in:
2024-10-02 21:38:07 +02:00
parent 61051ebdce
commit c94d4f67ea
9 changed files with 306 additions and 232 deletions

View File

@@ -14,11 +14,6 @@ $navbar-height: 64px;
.file-tree-container { .file-tree-container {
width: 100%; width: 100%;
& > div {
padding: 0;
margin: 0;
}
} }
} }

View File

@@ -0,0 +1,51 @@
import React from 'react';
import Editor from './Editor';
import MarkdownPreview from './MarkdownPreview';
import { getFileUrl } from '../services/api';
import { isImageFile } from '../utils/fileHelpers';
const ContentView = ({
activeTab,
content,
selectedFile,
onContentChange,
onSave,
themeType,
onLinkClick,
lookupFileByName,
}) => {
if (isImageFile(selectedFile)) {
return (
<div className="image-preview">
<img
src={getFileUrl(selectedFile)}
alt={selectedFile}
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
}}
/>
</div>
);
}
return activeTab === 'source' ? (
<Editor
content={content}
onChange={onContentChange}
onSave={onSave}
filePath={selectedFile}
themeType={themeType}
/>
) : (
<MarkdownPreview
content={content}
baseUrl={window.API_BASE_URL}
onLinkClick={onLinkClick}
lookupFileByName={lookupFileByName}
/>
);
};
export default ContentView;

View File

@@ -0,0 +1,78 @@
import React from 'react';
import { Button, Tooltip, ButtonGroup, Spacer } from '@geist-ui/core';
import { Plus, Trash, GitPullRequest, GitCommit } from '@geist-ui/icons';
const FileActions = ({
selectedFile,
gitEnabled,
gitAutoCommit,
onPull,
onCommitAndPush,
onCreateFile,
onDeleteFile,
}) => {
return (
<ButtonGroup className="file-actions">
<Tooltip text="Create new file" type="dark">
<Button
icon={<Plus />}
auto
scale={2 / 3}
onClick={onCreateFile}
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip
text={selectedFile ? 'Delete current file' : 'No file selected'}
type="dark"
>
<Button
icon={<Trash />}
auto
scale={2 / 3}
onClick={onDeleteFile}
disabled={!selectedFile}
type="error"
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip
text={gitEnabled ? 'Pull changes from remote' : 'Git is not enabled'}
type="dark"
>
<Button
icon={<GitPullRequest />}
auto
scale={2 / 3}
onClick={onPull}
disabled={!gitEnabled}
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip
text={
!gitEnabled
? 'Git is not enabled'
: gitAutoCommit
? 'Auto-commit is enabled'
: 'Commit and push changes'
}
type="dark"
>
<Button
icon={<GitCommit />}
auto
scale={2 / 3}
onClick={onCommitAndPush}
disabled={!gitEnabled || gitAutoCommit}
px={0.6}
/>
</Tooltip>
</ButtonGroup>
);
};
export default FileActions;

View File

@@ -1,26 +1,11 @@
import React from 'react'; import React from 'react';
import { Tree, Button, Tooltip, Spacer, ButtonGroup } from '@geist-ui/core'; import { Tree } from '@geist-ui/core';
import { import { File, Folder, Image } from '@geist-ui/icons';
File,
Folder,
GitPullRequest,
GitCommit,
Plus,
Trash,
Image,
} from '@geist-ui/icons';
import { isImageFile } from '../utils/fileHelpers';
const FileTree = ({ const FileTree = ({
files = [], files = [],
onFileSelect = () => {}, onFileSelect = () => {},
selectedFile = null, selectedFile = null,
gitEnabled = false,
gitAutoCommit = false,
onPull = () => {},
onCommitAndPush = () => {},
onCreateFile = () => {},
onDeleteFile = () => {},
}) => { }) => {
if (files.length === 0) { if (files.length === 0) {
return <div>No files to display</div>; return <div>No files to display</div>;
@@ -39,80 +24,23 @@ const FileTree = ({
); );
}; };
const isImageFile = (fileName) => {
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg'];
return imageExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
};
const renderIcon = ({ type, name }) => { const renderIcon = ({ type, name }) => {
if (type === 'directory') return <Folder />; if (type === 'directory') return <Folder />;
return isImageFile(name) ? <Image /> : <File />; return isImageFile(name) ? <Image /> : <File />;
}; };
return ( return (
<div> <Tree
<ButtonGroup className="file-tree-buttons"> value={files}
<Tooltip text="Create new file" type="dark"> onClick={handleSelect}
<Button renderIcon={renderIcon}
icon={<Plus />} renderLabel={renderLabel}
auto />
scale={2 / 3}
onClick={onCreateFile}
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip
text={selectedFile ? 'Delete current file' : 'No file selected'}
type="dark"
>
<Button
icon={<Trash />}
auto
scale={2 / 3}
onClick={onDeleteFile}
disabled={!selectedFile}
type="error"
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip
text={gitEnabled ? 'Pull changes from remote' : 'Git is not enabled'}
type="dark"
>
<Button
icon={<GitPullRequest />}
auto
scale={2 / 3}
onClick={onPull}
disabled={!gitEnabled}
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip
text={
!gitEnabled
? 'Git is not enabled'
: gitAutoCommit
? 'Auto-commit is enabled'
: 'Commit and push changes'
}
type="dark"
>
<Button
icon={<GitCommit />}
auto
scale={2 / 3}
onClick={onCommitAndPush}
disabled={!gitEnabled || gitAutoCommit}
px={0.6}
/>
</Tooltip>
</ButtonGroup>
<Tree
value={files}
onClick={handleSelect}
renderIcon={renderIcon}
renderLabel={renderLabel}
/>
</div>
); );
}; };

View File

@@ -11,16 +11,10 @@ import {
Button, Button,
} from '@geist-ui/core'; } from '@geist-ui/core';
import { Code, Eye } from '@geist-ui/icons'; import { Code, Eye } from '@geist-ui/icons';
import Editor from './Editor';
import FileTree from './FileTree'; import FileTree from './FileTree';
import MarkdownPreview from './MarkdownPreview'; import FileActions from './FileActions';
import { import ContentView from './ContentView';
commitAndPush, import { commitAndPush, saveFileContent, deleteFile } from '../services/api';
saveFileContent,
deleteFile,
getFileUrl,
lookupFileByName,
} from '../services/api';
import { isImageFile } from '../utils/fileHelpers'; import { isImageFile } from '../utils/fileHelpers';
const MainContent = ({ const MainContent = ({
@@ -33,33 +27,31 @@ const MainContent = ({
onContentChange, onContentChange,
onSave, onSave,
settings, settings,
onPullLatestChanges, pullLatestChanges,
onLinkClick, onLinkClick,
lookupFileByName,
}) => { }) => {
const [activeTab, setActiveTab] = useState('source'); const [activeTab, setActiveTab] = useState('source');
const { type: themeType } = useTheme(); const { type: themeType } = useTheme();
const { setToast } = useToasts(); const { setToast } = useToasts();
const [newFileModalVisible, setNewFileModalVisible] = useState(false); const [newFileModalVisible, setNewFileModalVisible] = useState(false);
const [newFileName, setNewFileName] = useState(''); const [newFileName, setNewFileName] = useState('');
const [isCurrentFileImage, setIsCurrentFileImage] = useState(false);
useEffect(() => { useEffect(() => {
const currentFileIsImage = isImageFile(selectedFile); if (isImageFile(selectedFile)) {
setIsCurrentFileImage(currentFileIsImage);
if (currentFileIsImage) {
setActiveTab('preview'); setActiveTab('preview');
} }
}, [selectedFile]); }, [selectedFile]);
const handleTabChange = (value) => { const handleTabChange = (value) => {
if (!isCurrentFileImage || value === 'preview') { if (!isImageFile(selectedFile) || value === 'preview') {
setActiveTab(value); setActiveTab(value);
} }
}; };
const handlePull = async () => { const handlePull = async () => {
try { try {
await onPullLatestChanges(); await pullLatestChanges();
setToast({ text: 'Successfully pulled latest changes', type: 'success' }); setToast({ text: 'Successfully pulled latest changes', type: 'success' });
} catch (error) { } catch (error) {
setToast({ setToast({
@@ -78,7 +70,7 @@ const MainContent = ({
text: 'Changes committed and pushed successfully', text: 'Changes committed and pushed successfully',
type: 'success', type: 'success',
}); });
await onPullLatestChanges(); // Pull changes after successful push await pullLatestChanges();
} }
} catch (error) { } catch (error) {
setToast({ setToast({
@@ -97,8 +89,8 @@ const MainContent = ({
try { try {
await saveFileContent(newFileName, ''); await saveFileContent(newFileName, '');
setToast({ text: 'New file created successfully', type: 'success' }); setToast({ text: 'New file created successfully', type: 'success' });
await onPullLatestChanges(); // Refresh file list await pullLatestChanges();
onFileSelect(newFileName); // Select the new file onFileSelect(newFileName);
} catch (error) { } catch (error) {
setToast({ setToast({
text: 'Failed to create new file: ' + error.message, text: 'Failed to create new file: ' + error.message,
@@ -119,8 +111,8 @@ const MainContent = ({
try { try {
await deleteFile(selectedFile); await deleteFile(selectedFile);
setToast({ text: 'File deleted successfully', type: 'success' }); setToast({ text: 'File deleted successfully', type: 'success' });
await onPullLatestChanges(); // Refresh file list await pullLatestChanges();
onFileSelect(null); // Deselect the file onFileSelect(null);
} catch (error) { } catch (error) {
setToast({ setToast({
text: 'Failed to delete file: ' + error.message, text: 'Failed to delete file: ' + error.message,
@@ -153,9 +145,7 @@ const MainContent = ({
<Grid.Container gap={1} height="calc(100vh - 64px)"> <Grid.Container gap={1} height="calc(100vh - 64px)">
<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">
<FileTree <FileActions
files={files}
onFileSelect={onFileSelect}
selectedFile={selectedFile} selectedFile={selectedFile}
gitEnabled={settings.gitEnabled} gitEnabled={settings.gitEnabled}
gitAutoCommit={settings.gitAutoCommit} gitAutoCommit={settings.gitAutoCommit}
@@ -164,6 +154,11 @@ const MainContent = ({
onCreateFile={handleCreateFile} onCreateFile={handleCreateFile}
onDeleteFile={handleDeleteFile} onDeleteFile={handleDeleteFile}
/> />
<FileTree
files={files}
onFileSelect={onFileSelect}
selectedFile={selectedFile}
/>
</div> </div>
</Grid> </Grid>
<Grid <Grid
@@ -180,40 +175,22 @@ const MainContent = ({
<Tabs.Item <Tabs.Item
label={<Code />} label={<Code />}
value="source" value="source"
disabled={isCurrentFileImage} disabled={isImageFile(selectedFile)}
/> />
<Tabs.Item label={<Eye />} value="preview" /> <Tabs.Item label={<Eye />} value="preview" />
</Tabs> </Tabs>
</div> </div>
<div className="content-body"> <div className="content-body">
{activeTab === 'source' && !isCurrentFileImage ? ( <ContentView
<Editor activeTab={activeTab}
content={content} content={content}
onChange={onContentChange} selectedFile={selectedFile}
onSave={onSave} onContentChange={onContentChange}
filePath={selectedFile} onSave={onSave}
themeType={themeType} themeType={themeType}
/> onLinkClick={onLinkClick}
) : isCurrentFileImage ? ( lookupFileByName={lookupFileByName}
<div className="image-preview"> />
<img
src={getFileUrl(selectedFile)}
alt={selectedFile}
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
}}
/>
</div>
) : (
<MarkdownPreview
content={content}
baseUrl={window.API_BASE_URL}
onLinkClick={onLinkClick}
lookupFileByName={lookupFileByName}
/>
)}
</div> </div>
</Grid> </Grid>
</Grid.Container> </Grid.Container>

View File

@@ -1,16 +1,9 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { import { Modal, Spacer, useTheme, Dot, useToasts } from '@geist-ui/core';
Modal,
Text,
Toggle,
Input,
Spacer,
useTheme,
Button,
Dot,
useToasts,
} from '@geist-ui/core';
import { saveUserSettings, fetchUserSettings } from '../services/api'; import { saveUserSettings, fetchUserSettings } from '../services/api';
import AppearanceSettings from './settings/AppearanceSettings';
import EditorSettings from './settings/EditorSettings';
import GitSettings from './settings/GitSettings';
const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => { const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
const theme = useTheme(); const theme = useTheme();
@@ -97,86 +90,25 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
)} )}
</Modal.Title> </Modal.Title>
<Modal.Content> <Modal.Content>
<div className="setting-group"> <AppearanceSettings
<Text h4>Appearance</Text> themeSettings={themeSettings}
<div className="setting-item"> onThemeChange={handleThemeChange}
<Text>Dark Mode</Text> />
<Toggle
checked={themeSettings === 'dark'}
onChange={handleThemeChange}
/>
</div>
</div>
<Spacer h={1} /> <Spacer h={1} />
<div className="setting-group"> <EditorSettings
<Text h4>Editor</Text> autoSave={settings.autoSave}
<div className="setting-item"> onAutoSaveChange={(value) => handleInputChange('autoSave', value)}
<Text>Auto Save</Text> />
<Toggle
checked={settings.autoSave}
onChange={(e) => handleInputChange('autoSave', e.target.checked)}
/>
</div>
</div>
<Spacer h={1} /> <Spacer h={1} />
<div className="setting-group"> <GitSettings
<Text h4>Git Integration</Text> gitEnabled={settings.gitEnabled}
<div className="setting-item"> gitUrl={settings.gitUrl}
<Text>Enable Git</Text> gitUser={settings.gitUser}
<Toggle gitToken={settings.gitToken}
checked={settings.gitEnabled} gitAutoCommit={settings.gitAutoCommit}
onChange={(e) => gitCommitMsgTemplate={settings.gitCommitMsgTemplate}
handleInputChange('gitEnabled', e.target.checked) onInputChange={handleInputChange}
} />
/>
</div>
<div className={settings.gitEnabled ? '' : 'disabled'}>
<Input
width="100%"
label="Git URL"
value={settings.gitUrl}
onChange={(e) => handleInputChange('gitUrl', e.target.value)}
disabled={!settings.gitEnabled}
/>
<Spacer h={0.5} />
<Input
width="100%"
label="Git Username"
value={settings.gitUser}
onChange={(e) => handleInputChange('gitUser', e.target.value)}
disabled={!settings.gitEnabled}
/>
<Spacer h={0.5} />
<Input.Password
width="100%"
label="Git Token"
value={settings.gitToken}
onChange={(e) => handleInputChange('gitToken', e.target.value)}
disabled={!settings.gitEnabled}
/>
<Spacer h={0.5} />
<div className="setting-item">
<Text>Auto Commit</Text>
<Toggle
checked={settings.gitAutoCommit}
onChange={(e) =>
handleInputChange('gitAutoCommit', e.target.checked)
}
disabled={!settings.gitEnabled}
/>
</div>
<Spacer h={0.5} />
<Input
width="100%"
label="Commit Message Template"
value={settings.gitCommitMsgTemplate}
onChange={(e) =>
handleInputChange('gitCommitMsgTemplate', e.target.value)
}
disabled={!settings.gitEnabled}
/>
</div>
</div>
</Modal.Content> </Modal.Content>
<Modal.Action passive onClick={onClose}> <Modal.Action passive onClick={onClose}>
Cancel Cancel

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { Text, Toggle } from '@geist-ui/core';
const AppearanceSettings = ({ themeSettings, onThemeChange }) => {
return (
<div className="setting-group">
<Text h4>Appearance</Text>
<div className="setting-item">
<Text>Dark Mode</Text>
<Toggle checked={themeSettings === 'dark'} onChange={onThemeChange} />
</div>
</div>
);
};
export default AppearanceSettings;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import { Text, Toggle, Tooltip } from '@geist-ui/core';
const EditorSettings = ({ autoSave, onAutoSaveChange }) => {
return (
<div className="setting-group">
<Text h4>Editor</Text>
<div className="setting-item">
<Text>Auto Save</Text>
<Tooltip
text="Auto Save feature is coming soon!"
type="dark"
placement="left"
>
<Toggle
checked={autoSave}
onChange={(e) => onAutoSaveChange(e.target.checked)}
disabled
/>
</Tooltip>
</div>
</div>
);
};
export default EditorSettings;

View File

@@ -0,0 +1,71 @@
import React from 'react';
import { Text, Toggle, Input, Spacer } from '@geist-ui/core';
const GitSettings = ({
gitEnabled,
gitUrl,
gitUser,
gitToken,
gitAutoCommit,
gitCommitMsgTemplate,
onInputChange,
}) => {
return (
<div className="setting-group">
<Text h4>Git Integration</Text>
<div className="setting-item">
<Text>Enable Git</Text>
<Toggle
checked={gitEnabled}
onChange={(e) => onInputChange('gitEnabled', e.target.checked)}
/>
</div>
<div className={gitEnabled ? '' : 'disabled'}>
<Input
width="100%"
label="Git URL"
value={gitUrl}
onChange={(e) => onInputChange('gitUrl', e.target.value)}
disabled={!gitEnabled}
/>
<Spacer h={0.5} />
<Input
width="100%"
label="Git Username"
value={gitUser}
onChange={(e) => onInputChange('gitUser', e.target.value)}
disabled={!gitEnabled}
/>
<Spacer h={0.5} />
<Input.Password
width="100%"
label="Git Token"
value={gitToken}
onChange={(e) => onInputChange('gitToken', e.target.value)}
disabled={!gitEnabled}
/>
<Spacer h={0.5} />
<div className="setting-item">
<Text>Auto Commit</Text>
<Toggle
checked={gitAutoCommit}
onChange={(e) => onInputChange('gitAutoCommit', e.target.checked)}
disabled={!gitEnabled}
/>
</div>
<Spacer h={0.5} />
<Input
width="100%"
label="Commit Message Template"
value={gitCommitMsgTemplate}
onChange={(e) =>
onInputChange('gitCommitMsgTemplate', e.target.value)
}
disabled={!gitEnabled}
/>
</div>
</div>
);
};
export default GitSettings;