Set up and run eslint and prettier

This commit is contained in:
2024-09-29 21:52:30 +02:00
parent eb497c7ccb
commit 05f5ca2877
15 changed files with 359 additions and 214 deletions

31
.eslintrc.json Normal file
View File

@@ -0,0 +1,31 @@
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"react/prop-types": "off",
"no-unused-vars": "warn"
},
"settings": {
"react": {
"version": "detect"
}
}
}

7
.prettierrc.json Normal file
View File

@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -13,7 +13,6 @@
}, },
"keywords": [ "keywords": [
"markdown", "markdown",
"hypermd",
"editor" "editor"
], ],
"author": "Matúš Námešný", "author": "Matúš Námešný",

View File

@@ -47,7 +47,7 @@ function App() {
<CssBaseline /> <CssBaseline />
<Page> <Page>
<Header currentTheme={themeType} onThemeChange={setTheme} /> <Header currentTheme={themeType} onThemeChange={setTheme} />
<Page.Content className='page-content'> <Page.Content className="page-content">
<MainContent <MainContent
content={content} content={content}
files={files} files={files}

View File

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

View File

@@ -1,10 +1,10 @@
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from 'react';
import { basicSetup } from "codemirror"; import { basicSetup } from 'codemirror';
import { EditorState } from "@codemirror/state"; import { EditorState } from '@codemirror/state';
import { EditorView, keymap } from "@codemirror/view"; 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';
const Editor = ({ content, onChange, onSave, filePath, themeType }) => { const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
const editorRef = useRef(); const editorRef = useRef();
@@ -17,20 +17,20 @@ const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
}; };
const theme = EditorView.theme({ const theme = EditorView.theme({
"&": { '&': {
height: "100%", height: '100%',
fontSize: "14px", fontSize: '14px',
}, },
".cm-scroller": { '.cm-scroller': {
overflow: "auto", overflow: 'auto',
}, },
".cm-gutters": { '.cm-gutters': {
backgroundColor: themeType === "dark" ? "#1e1e1e" : "#f5f5f5", backgroundColor: themeType === 'dark' ? '#1e1e1e' : '#f5f5f5',
color: themeType === "dark" ? "#858585" : "#999", color: themeType === 'dark' ? '#858585' : '#999',
border: "none", border: 'none',
}, },
".cm-activeLineGutter": { '.cm-activeLineGutter': {
backgroundColor: themeType === "dark" ? "#2c313a" : "#e8e8e8", backgroundColor: themeType === 'dark' ? '#2c313a' : '#e8e8e8',
}, },
}); });
@@ -41,18 +41,20 @@ const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
markdown(), markdown(),
EditorView.lineWrapping, EditorView.lineWrapping,
keymap.of(defaultKeymap), keymap.of(defaultKeymap),
keymap.of([{ keymap.of([
key: "Ctrl-s", {
run: handleSave, key: 'Ctrl-s',
preventDefault: true run: handleSave,
}]), preventDefault: true,
},
]),
EditorView.updateListener.of((update) => { EditorView.updateListener.of((update) => {
if (update.docChanged) { if (update.docChanged) {
onChange(update.state.doc.toString()); onChange(update.state.doc.toString());
} }
}), }),
theme, theme,
themeType === "dark" ? oneDark : [], themeType === 'dark' ? oneDark : [],
], ],
}); });
@@ -71,7 +73,11 @@ const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
useEffect(() => { useEffect(() => {
if (viewRef.current && content !== viewRef.current.state.doc.toString()) { if (viewRef.current && content !== viewRef.current.state.doc.toString()) {
viewRef.current.dispatch({ viewRef.current.dispatch({
changes: { from: 0, to: viewRef.current.state.doc.length, insert: content } changes: {
from: 0,
to: viewRef.current.state.doc.length,
insert: content,
},
}); });
} }
}, [content]); }, [content]);

View File

@@ -1,6 +1,13 @@
import React from 'react'; import React from 'react';
import { Tree, Button, Tooltip, Spacer, ButtonGroup } from '@geist-ui/core'; import { Tree, Button, Tooltip, Spacer, ButtonGroup } from '@geist-ui/core';
import { File, Folder, GitPullRequest, GitCommit, Plus, Trash } from '@geist-ui/icons'; import {
File,
Folder,
GitPullRequest,
GitCommit,
Plus,
Trash,
} from '@geist-ui/icons';
const FileTree = ({ const FileTree = ({
files = [], files = [],
@@ -11,7 +18,7 @@ const FileTree = ({
onPull = () => {}, onPull = () => {},
onCommitAndPush = () => {}, onCommitAndPush = () => {},
onCreateFile = () => {}, onCreateFile = () => {},
onDeleteFile = () => {} onDeleteFile = () => {},
}) => { }) => {
if (files.length === 0) { if (files.length === 0) {
return <div>No files to display</div>; return <div>No files to display</div>;
@@ -30,26 +37,30 @@ const FileTree = ({
); );
}; };
const renderIcon = ({ type }) => type === 'directory' ? <Folder /> : <File />; const renderIcon = ({ type }) =>
type === 'directory' ? <Folder /> : <File />;
return ( return (
<div> <div>
<ButtonGroup className='file-tree-buttons'> <ButtonGroup className="file-tree-buttons">
<Tooltip text="Create new file" type="dark"> <Tooltip text="Create new file" type="dark">
<Button <Button
icon={<Plus />} icon={<Plus />}
auto auto
scale={2/3} scale={2 / 3}
onClick={onCreateFile} onClick={onCreateFile}
px={0.6} px={0.6}
/> />
</Tooltip> </Tooltip>
<Spacer w={0.5} /> <Spacer w={0.5} />
<Tooltip text={selectedFile ? "Delete current file" : "No file selected"} type="dark"> <Tooltip
text={selectedFile ? 'Delete current file' : 'No file selected'}
type="dark"
>
<Button <Button
icon={<Trash />} icon={<Trash />}
auto auto
scale={2/3} scale={2 / 3}
onClick={onDeleteFile} onClick={onDeleteFile}
disabled={!selectedFile} disabled={!selectedFile}
type="error" type="error"
@@ -57,26 +68,34 @@ const FileTree = ({
/> />
</Tooltip> </Tooltip>
<Spacer w={0.5} /> <Spacer w={0.5} />
<Tooltip text={gitEnabled ? "Pull changes from remote" : "Git is not enabled"} type="dark"> <Tooltip
text={gitEnabled ? 'Pull changes from remote' : 'Git is not enabled'}
type="dark"
>
<Button <Button
icon={<GitPullRequest />} icon={<GitPullRequest />}
auto auto
scale={2/3} scale={2 / 3}
onClick={onPull} onClick={onPull}
disabled={!gitEnabled} disabled={!gitEnabled}
px={0.6} px={0.6}
/> />
</Tooltip> </Tooltip>
<Spacer w={0.5} /> <Spacer w={0.5} />
<Tooltip text={ <Tooltip
!gitEnabled ? "Git is not enabled" : text={
gitAutoCommit ? "Auto-commit is enabled" : !gitEnabled
"Commit and push changes" ? 'Git is not enabled'
} type="dark"> : gitAutoCommit
? 'Auto-commit is enabled'
: 'Commit and push changes'
}
type="dark"
>
<Button <Button
icon={<GitCommit />} icon={<GitCommit />}
auto auto
scale={2/3} scale={2 / 3}
onClick={onCommitAndPush} onClick={onCommitAndPush}
disabled={!gitEnabled || gitAutoCommit} disabled={!gitEnabled || gitAutoCommit}
px={0.6} px={0.6}

View File

@@ -1,5 +1,15 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Grid, Breadcrumbs, Tabs, Dot, useTheme, useToasts, Modal, Input, Button } from '@geist-ui/core'; import {
Grid,
Breadcrumbs,
Tabs,
Dot,
useTheme,
useToasts,
Modal,
Input,
Button,
} from '@geist-ui/core';
import { Code, Eye } from '@geist-ui/icons'; import { Code, Eye } from '@geist-ui/icons';
import Editor from './Editor'; import Editor from './Editor';
import FileTree from './FileTree'; import FileTree from './FileTree';
@@ -29,7 +39,10 @@ const MainContent = ({
await pullLatestChanges(); await pullLatestChanges();
setToast({ text: 'Successfully pulled latest changes', type: 'success' }); setToast({ text: 'Successfully pulled latest changes', type: 'success' });
} catch (error) { } catch (error) {
setToast({ text: 'Failed to pull changes: ' + error.message, type: 'error' }); setToast({
text: 'Failed to pull changes: ' + error.message,
type: 'error',
});
} }
}; };
@@ -38,11 +51,17 @@ const MainContent = ({
const message = prompt('Enter commit message:'); const message = prompt('Enter commit message:');
if (message) { if (message) {
await commitAndPush(message); await commitAndPush(message);
setToast({ text: 'Changes committed and pushed successfully', type: 'success' }); setToast({
text: 'Changes committed and pushed successfully',
type: 'success',
});
await pullLatestChanges(); // Pull changes after successful push await pullLatestChanges(); // Pull changes after successful push
} }
} catch (error) { } catch (error) {
setToast({ text: 'Failed to commit and push changes: ' + error.message, type: 'error' }); setToast({
text: 'Failed to commit and push changes: ' + error.message,
type: 'error',
});
} }
}; };
@@ -58,7 +77,10 @@ const MainContent = ({
await pullLatestChanges(); // Refresh file list await pullLatestChanges(); // Refresh file list
onFileSelect(newFileName); // Select the new file onFileSelect(newFileName); // Select the new file
} catch (error) { } catch (error) {
setToast({ text: 'Failed to create new file: ' + error.message, type: 'error' }); setToast({
text: 'Failed to create new file: ' + error.message,
type: 'error',
});
} }
} }
setNewFileModalVisible(false); setNewFileModalVisible(false);
@@ -67,7 +89,9 @@ const MainContent = ({
const handleDeleteFile = async () => { const handleDeleteFile = async () => {
if (selectedFile) { if (selectedFile) {
const confirmDelete = window.confirm(`Are you sure you want to delete "${selectedFile}"?`); const confirmDelete = window.confirm(
`Are you sure you want to delete "${selectedFile}"?`
);
if (confirmDelete) { if (confirmDelete) {
try { try {
await deleteFile(selectedFile); await deleteFile(selectedFile);
@@ -75,7 +99,10 @@ const MainContent = ({
await pullLatestChanges(); // Refresh file list await pullLatestChanges(); // Refresh file list
onFileSelect(null); // Deselect the file onFileSelect(null); // Deselect the file
} catch (error) { } catch (error) {
setToast({ text: 'Failed to delete file: ' + error.message, type: 'error' }); setToast({
text: 'Failed to delete file: ' + error.message,
type: 'error',
});
} }
} }
} }
@@ -91,7 +118,9 @@ const MainContent = ({
<Breadcrumbs.Item key={index}>{part}</Breadcrumbs.Item> <Breadcrumbs.Item key={index}>{part}</Breadcrumbs.Item>
))} ))}
</Breadcrumbs> </Breadcrumbs>
{hasUnsavedChanges && <Dot type="warning" className="unsaved-indicator" />} {hasUnsavedChanges && (
<Dot type="warning" className="unsaved-indicator" />
)}
</div> </div>
); );
}; };
@@ -114,7 +143,14 @@ const MainContent = ({
/> />
</div> </div>
</Grid> </Grid>
<Grid xs={24} sm={18} md={19} lg={20} height="100%" className="main-content"> <Grid
xs={24}
sm={18}
md={19}
lg={20}
height="100%"
className="main-content"
>
<div className="content-header"> <div className="content-header">
{renderBreadcrumbs()} {renderBreadcrumbs()}
<Tabs value={activeTab} onChange={setActiveTab}> <Tabs value={activeTab} onChange={setActiveTab}>
@@ -137,7 +173,10 @@ const MainContent = ({
</div> </div>
</Grid> </Grid>
</Grid.Container> </Grid.Container>
<Modal visible={newFileModalVisible} onClose={() => setNewFileModalVisible(false)}> <Modal
visible={newFileModalVisible}
onClose={() => setNewFileModalVisible(false)}
>
<Modal.Title>Create New File</Modal.Title> <Modal.Title>Create New File</Modal.Title>
<Modal.Content> <Modal.Content>
<Input <Input
@@ -147,7 +186,9 @@ const MainContent = ({
onChange={(e) => setNewFileName(e.target.value)} onChange={(e) => setNewFileName(e.target.value)}
/> />
</Modal.Content> </Modal.Content>
<Modal.Action passive onClick={() => setNewFileModalVisible(false)}>Cancel</Modal.Action> <Modal.Action passive onClick={() => setNewFileModalVisible(false)}>
Cancel
</Modal.Action>
<Modal.Action onClick={handleNewFileSubmit}>Create</Modal.Action> <Modal.Action onClick={handleNewFileSubmit}>Create</Modal.Action>
</Modal> </Modal>
</> </>

View File

@@ -13,8 +13,8 @@ const MarkdownPreview = ({ content }) => {
remarkPlugins={[remarkMath]} remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]} rehypePlugins={[rehypeKatex]}
components={{ components={{
code({node, inline, className, children, ...props}) { code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '') const match = /language-(\w+)/.exec(className || '');
return !inline && match ? ( return !inline && match ? (
<SyntaxHighlighter <SyntaxHighlighter
style={vscDarkPlus} style={vscDarkPlus}
@@ -28,8 +28,8 @@ const MarkdownPreview = ({ content }) => {
<code className={className} {...props}> <code className={className} {...props}>
{children} {children}
</code> </code>
) );
} },
}} }}
> >
{content} {content}

View File

@@ -1,5 +1,15 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { Modal, Text, Toggle, Input, Spacer, useTheme, Button, Dot, useToasts } from '@geist-ui/core'; import {
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';
const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => { const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
@@ -42,13 +52,14 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
}, [isInitialized, loadSettings]); }, [isInitialized, loadSettings]);
useEffect(() => { useEffect(() => {
const settingsChanged = JSON.stringify(settings) !== JSON.stringify(originalSettings); const settingsChanged =
JSON.stringify(settings) !== JSON.stringify(originalSettings);
const themeChanged = themeSettings !== originalTheme; const themeChanged = themeSettings !== originalTheme;
setHasUnsavedChanges(settingsChanged || themeChanged); setHasUnsavedChanges(settingsChanged || themeChanged);
}, [settings, themeSettings, originalSettings, originalTheme]); }, [settings, themeSettings, originalSettings, originalTheme]);
const handleInputChange = (key, value) => { const handleInputChange = (key, value) => {
setSettings(prev => ({ ...prev, [key]: value })); setSettings((prev) => ({ ...prev, [key]: value }));
}; };
const handleThemeChange = () => { const handleThemeChange = () => {
@@ -70,7 +81,10 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
onClose(); onClose();
} catch (error) { } catch (error) {
console.error('Failed to save settings:', error); console.error('Failed to save settings:', error);
setToast({ text: 'Failed to save settings: ' + error.message, type: 'error' }); setToast({
text: 'Failed to save settings: ' + error.message,
type: 'error',
});
} }
}; };
@@ -111,7 +125,9 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
<Text>Enable Git</Text> <Text>Enable Git</Text>
<Toggle <Toggle
checked={settings.gitEnabled} checked={settings.gitEnabled}
onChange={(e) => handleInputChange('gitEnabled', e.target.checked)} onChange={(e) =>
handleInputChange('gitEnabled', e.target.checked)
}
/> />
</div> </div>
<div className={settings.gitEnabled ? '' : 'disabled'}> <div className={settings.gitEnabled ? '' : 'disabled'}>
@@ -143,7 +159,9 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
<Text>Auto Commit</Text> <Text>Auto Commit</Text>
<Toggle <Toggle
checked={settings.gitAutoCommit} checked={settings.gitAutoCommit}
onChange={(e) => handleInputChange('gitAutoCommit', e.target.checked)} onChange={(e) =>
handleInputChange('gitAutoCommit', e.target.checked)
}
disabled={!settings.gitEnabled} disabled={!settings.gitEnabled}
/> />
</div> </div>
@@ -152,13 +170,17 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
width="100%" width="100%"
label="Commit Message Template" label="Commit Message Template"
value={settings.gitCommitMsgTemplate} value={settings.gitCommitMsgTemplate}
onChange={(e) => handleInputChange('gitCommitMsgTemplate', e.target.value)} onChange={(e) =>
handleInputChange('gitCommitMsgTemplate', e.target.value)
}
disabled={!settings.gitEnabled} disabled={!settings.gitEnabled}
/> />
</div> </div>
</div> </div>
</Modal.Content> </Modal.Content>
<Modal.Action passive onClick={onClose}>Cancel</Modal.Action> <Modal.Action passive onClick={onClose}>
Cancel
</Modal.Action>
<Modal.Action onClick={handleSubmit}>Save Changes</Modal.Action> <Modal.Action onClick={handleSubmit}>Save Changes</Modal.Action>
</Modal> </Modal>
); );

View File

@@ -1,11 +1,16 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { useToasts } from '@geist-ui/core'; import { useToasts } from '@geist-ui/core';
import { fetchFileList, fetchFileContent, saveFileContent, pullChanges } from '../services/api'; import {
fetchFileList,
fetchFileContent,
saveFileContent,
pullChanges,
} from '../services/api';
const DEFAULT_FILE = { const DEFAULT_FILE = {
name: 'New File.md', name: 'New File.md',
path: 'New File.md', path: 'New File.md',
content: '# Welcome to NovaMD\n\nStart editing here!' content: '# Welcome to NovaMD\n\nStart editing here!',
}; };
const useFileManagement = (gitEnabled = false) => { const useFileManagement = (gitEnabled = false) => {
@@ -35,11 +40,17 @@ const useFileManagement = (gitEnabled = false) => {
if (gitEnabled) { if (gitEnabled) {
try { try {
await pullChanges(); await pullChanges();
setToast({ text: 'Latest changes pulled successfully', type: 'success' }); setToast({
text: 'Latest changes pulled successfully',
type: 'success',
});
await loadFileList(); // Reload file list after pulling changes await loadFileList(); // Reload file list after pulling changes
} 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: ' + error.message, type: 'error' }); setToast({
text: 'Failed to pull latest changes: ' + error.message,
type: 'error',
});
} }
} }
}, [gitEnabled, loadFileList, setToast]); }, [gitEnabled, loadFileList, setToast]);
@@ -58,7 +69,9 @@ const useFileManagement = (gitEnabled = false) => {
const handleFileSelect = async (filePath) => { const handleFileSelect = async (filePath) => {
if (hasUnsavedChanges) { if (hasUnsavedChanges) {
const confirmSwitch = window.confirm('You have unsaved changes. Are you sure you want to switch files?'); const confirmSwitch = window.confirm(
'You have unsaved changes. Are you sure you want to switch files?'
);
if (!confirmSwitch) return; if (!confirmSwitch) return;
} }
@@ -80,20 +93,26 @@ const useFileManagement = (gitEnabled = false) => {
setHasUnsavedChanges(true); setHasUnsavedChanges(true);
}; };
const handleSave = useCallback(async (filePath, fileContent) => { const handleSave = useCallback(
try { async (filePath, fileContent) => {
await saveFileContent(filePath, fileContent); try {
setToast({ text: 'File saved successfully', type: 'success' }); await saveFileContent(filePath, fileContent);
setIsNewFile(false); setToast({ text: 'File saved successfully', type: 'success' });
setHasUnsavedChanges(false); setIsNewFile(false);
if (isNewFile) { setHasUnsavedChanges(false);
await loadFileList(); if (isNewFile) {
await loadFileList();
}
} catch (error) {
console.error('Error saving file:', error);
setToast({
text: 'Failed to save file. Please try again.',
type: 'error',
});
} }
} catch (error) { },
console.error('Error saving file:', error); [setToast, isNewFile, loadFileList]
setToast({ text: 'Failed to save file. Please try again.', type: 'error' }); );
}
}, [setToast, isNewFile, loadFileList]);
return { return {
content, content,

View File

@@ -1,8 +1,8 @@
import React from "react"; import React from 'react';
import ReactDOM from "react-dom/client"; import ReactDOM from 'react-dom/client';
import App from "./App"; import App from './App';
const root = ReactDOM.createRoot(document.getElementById("root")); const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />

View File

@@ -1,127 +1,129 @@
const API_BASE_URL = window.API_BASE_URL; const API_BASE_URL = window.API_BASE_URL;
export const fetchFileList = async () => { export const fetchFileList = async () => {
try { try {
const response = await fetch(`${API_BASE_URL}/files`); const response = await fetch(`${API_BASE_URL}/files`);
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to fetch file list'); throw new Error('Failed to fetch file list');
}
return await response.json();
} catch (error) {
console.error('Error fetching file list:', error);
throw error;
} }
return await response.json();
} catch (error) {
console.error('Error fetching file list:', error);
throw error;
}
}; };
export const fetchFileContent = async (filePath) => { export const fetchFileContent = async (filePath) => {
try { try {
const response = await fetch(`${API_BASE_URL}/files/${filePath}`); const response = await fetch(`${API_BASE_URL}/files/${filePath}`);
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to fetch file content'); throw new Error('Failed to fetch file content');
}
return await response.text();
} catch (error) {
console.error('Error fetching file content:', error);
throw error;
} }
return await response.text();
} catch (error) {
console.error('Error fetching file content:', error);
throw error;
}
}; };
export const saveFileContent = async (filePath, content) => { export const saveFileContent = async (filePath, content) => {
const response = await fetch(`${API_BASE_URL}/files/${filePath}`, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: content,
});
if (!response.ok) {
throw new Error('Failed to save file');
}
return await response.text();
};
export const deleteFile = async (filePath) => {
try {
const response = await fetch(`${API_BASE_URL}/files/${filePath}`, { const response = await fetch(`${API_BASE_URL}/files/${filePath}`, {
method: 'POST', method: 'DELETE',
headers: {
'Content-Type': 'text/plain',
},
body: content,
}); });
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to save file'); throw new Error('Failed to delete file');
} }
return await response.text(); return await response.text();
}; } catch (error) {
console.error('Error deleting file:', error);
throw error;
}
};
export const deleteFile = async (filePath) => { export const fetchUserSettings = async (userId) => {
try { try {
const response = await fetch(`${API_BASE_URL}/files/${filePath}`, { const response = await fetch(`${API_BASE_URL}/settings?userId=${userId}`);
method: 'DELETE', if (!response.ok) {
}); throw new Error('Failed to fetch user settings');
if (!response.ok) {
throw new Error('Failed to delete file');
}
return await response.text();
} catch (error) {
console.error('Error deleting file:', error);
throw error;
}
};
export const fetchUserSettings = async (userId) => {
try {
const response = await fetch(`${API_BASE_URL}/settings?userId=${userId}`);
if (!response.ok) {
throw new Error('Failed to fetch user settings');
}
return await response.json();
} catch (error) {
console.error('Error fetching user settings:', error);
throw error;
} }
return await response.json();
} catch (error) {
console.error('Error fetching user settings:', error);
throw error;
}
}; };
export const saveUserSettings = async (settings) => { export const saveUserSettings = async (settings) => {
try { try {
const response = await fetch(`${API_BASE_URL}/settings`, { const response = await fetch(`${API_BASE_URL}/settings`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify(settings), body: JSON.stringify(settings),
}); });
if (!response.ok) { if (!response.ok) {
const errorData = await response.json().catch(() => null); const errorData = await response.json().catch(() => null);
throw new Error(errorData?.message || `HTTP error! status: ${response.status}`); throw new Error(
} errorData?.message || `HTTP error! status: ${response.status}`
);
return await response.json();
} catch (error) {
console.error('Error saving user settings:', error);
throw error;
} }
return await response.json();
} catch (error) {
console.error('Error saving user settings:', error);
throw error;
}
}; };
export const pullChanges = async () => { export const pullChanges = async () => {
try { try {
const response = await fetch(`${API_BASE_URL}/git/pull`, { const response = await fetch(`${API_BASE_URL}/git/pull`, {
method: 'POST', method: 'POST',
}); });
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to pull changes'); throw new Error('Failed to pull changes');
}
return await response.json();
} catch (error) {
console.error('Error pulling changes:', error);
throw error;
} }
}; return await response.json();
} catch (error) {
console.error('Error pulling changes:', error);
throw error;
}
};
export const commitAndPush = async (message) => { export const commitAndPush = async (message) => {
try { try {
const response = await fetch(`${API_BASE_URL}/git/commit`, { const response = await fetch(`${API_BASE_URL}/git/commit`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ message }), body: JSON.stringify({ message }),
}); });
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to commit and push changes'); throw new Error('Failed to commit and push changes');
}
return await response.json();
} catch (error) {
console.error('Error committing and pushing changes:', error);
throw error;
} }
}; return await response.json();
} catch (error) {
console.error('Error committing and pushing changes:', error);
throw error;
}
};