Refactor imports and handle async operations in components

This commit is contained in:
2025-05-23 22:13:25 +02:00
parent 646a897b93
commit ad2334c414
5 changed files with 59 additions and 51 deletions

View File

@@ -1,5 +1,4 @@
import type { FormEvent} from 'react'; import React, { useState, type FormEvent } from 'react';
import React, { useState } from 'react';
import { import {
TextInput, TextInput,
PasswordInput, PasswordInput,

View File

@@ -26,7 +26,7 @@ const Editor: React.FC<EditorProps> = ({
useEffect(() => { useEffect(() => {
const handleEditorSave = (view: EditorView): boolean => { const handleEditorSave = (view: EditorView): boolean => {
handleSave(selectedFile, view.state.doc.toString()); void handleSave(selectedFile, view.state.doc.toString());
return true; return true;
}; };
@@ -85,6 +85,8 @@ const Editor: React.FC<EditorProps> = ({
view.destroy(); view.destroy();
viewRef.current = null; viewRef.current = null;
}; };
// TODO: Refactor
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [colorScheme, handleContentChange, handleSave, selectedFile]); }, [colorScheme, handleContentChange, handleSave, selectedFile]);
useEffect(() => { useEffect(() => {

View File

@@ -1,11 +1,10 @@
import type { ReactNode } from 'react'; import React, { useState, useEffect, useMemo, type ReactNode } from 'react';
import React, { useState, useEffect, useMemo } from 'react'; import { unified, type Preset } from 'unified';
import { unified } from 'unified';
import remarkParse from 'remark-parse'; import remarkParse from 'remark-parse';
import remarkMath from 'remark-math'; import remarkMath from 'remark-math';
import remarkRehype from 'remark-rehype'; import remarkRehype from 'remark-rehype';
import rehypeMathjax from 'rehype-mathjax'; import rehypeMathjax from 'rehype-mathjax';
import rehypeReact from 'rehype-react'; import rehypeReact, { type Options } from 'rehype-react';
import rehypePrism from 'rehype-prism'; import rehypePrism from 'rehype-prism';
import * as prod from 'react/jsx-runtime'; import * as prod from 'react/jsx-runtime';
import { notifications } from '@mantine/notifications'; import { notifications } from '@mantine/notifications';
@@ -20,19 +19,19 @@ interface MarkdownPreviewProps {
interface MarkdownImageProps { interface MarkdownImageProps {
src: string; src: string;
alt?: string; alt?: string;
[key: string]: any; [key: string]: unknown;
} }
interface MarkdownLinkProps { interface MarkdownLinkProps {
href: string; href: string;
children: ReactNode; children: ReactNode;
[key: string]: any; [key: string]: unknown;
} }
interface MarkdownCodeProps { interface MarkdownCodeProps {
children: ReactNode; children: ReactNode;
className?: string; className?: string;
[key: string]: any; [key: string]: unknown;
} }
const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
@@ -45,6 +44,7 @@ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
const baseUrl = window.API_BASE_URL; const baseUrl = window.API_BASE_URL;
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const processor = useMemo(() => {
const handleLinkClick = ( const handleLinkClick = (
e: React.MouseEvent<HTMLAnchorElement>, e: React.MouseEvent<HTMLAnchorElement>,
href: string href: string
@@ -57,7 +57,7 @@ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
href.replace(`${baseUrl}/internal/`, '') href.replace(`${baseUrl}/internal/`, '')
).split('#'); ).split('#');
if (filePath) { if (filePath) {
handleFileSelect(filePath); void handleFileSelect(filePath);
} }
} else if (href.startsWith(`${baseUrl}/notfound/`)) { } else if (href.startsWith(`${baseUrl}/notfound/`)) {
// For non-existent files, show a notification // For non-existent files, show a notification
@@ -71,8 +71,6 @@ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
}); });
} }
}; };
const processor = useMemo(() => {
// Only create the processor if we have a workspace name // Only create the processor if we have a workspace name
if (!currentWorkspace?.name) { if (!currentWorkspace?.name) {
return unified(); return unified();
@@ -80,16 +78,18 @@ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
return unified() return unified()
.use(remarkParse) .use(remarkParse)
.use(remarkWikiLinks, currentWorkspace.name) // Now we know this is defined .use(remarkWikiLinks, currentWorkspace.name)
.use(remarkMath) .use(remarkMath)
.use(remarkRehype) .use(remarkRehype)
.use(rehypeMathjax) .use(rehypeMathjax)
.use(rehypePrism) .use(rehypePrism as Preset)
.use(rehypeReact as any, { .use(rehypeReact, {
production: true,
jsx: prod.jsx, jsx: prod.jsx,
jsxs: prod.jsxs, jsxs: prod.jsxs,
Fragment: prod.Fragment, Fragment: prod.Fragment,
development: false,
elementAttributeNameCase: 'react',
stylePropertyNameCase: 'dom',
components: { components: {
img: ({ src, alt, ...props }: MarkdownImageProps) => ( img: ({ src, alt, ...props }: MarkdownImageProps) => (
<img <img
@@ -115,8 +115,8 @@ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
); );
}, },
}, },
}); } as Options);
}, [baseUrl, handleFileSelect, currentWorkspace?.name]); }, [currentWorkspace?.name, baseUrl, handleFileSelect]);
useEffect(() => { useEffect(() => {
const processContent = async (): Promise<void> => { const processContent = async (): Promise<void> => {
@@ -132,7 +132,7 @@ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
} }
}; };
processContent(); void processContent();
}, [content, processor, currentWorkspace]); }, [content, processor, currentWorkspace]);
return <div className="markdown-preview">{processedContent}</div>; return <div className="markdown-preview">{processedContent}</div>;

View File

@@ -61,7 +61,11 @@ const FileActions: React.FC<FileActionsProps> = ({
<ActionIcon <ActionIcon
variant="default" variant="default"
size="md" size="md"
onClick={handlePullChanges} onClick={() => {
handlePullChanges().catch((error) => {
console.error('Error pulling changes:', error);
});
}}
disabled={!settings.gitEnabled} disabled={!settings.gitEnabled}
> >
<IconGitPullRequest size={16} /> <IconGitPullRequest size={16} />

View File

@@ -1,10 +1,9 @@
import React, { useRef, useState, useLayoutEffect } from 'react'; import React, { useRef, useState, useLayoutEffect } from 'react';
import type { NodeApi } from 'react-arborist'; import { Tree, type NodeApi } from 'react-arborist';
import { Tree } from 'react-arborist';
import { IconFile, IconFolder, IconFolderOpen } from '@tabler/icons-react'; import { IconFile, IconFolder, IconFolderOpen } from '@tabler/icons-react';
import { Tooltip } from '@mantine/core'; import { Tooltip } from '@mantine/core';
import useResizeObserver from '@react-hook/resize-observer'; import useResizeObserver from '@react-hook/resize-observer';
import type { FileNode } from '../../types/fileApi'; import type { FileNode } from '@/types/models';
interface Size { interface Size {
width: number; width: number;
@@ -42,7 +41,11 @@ const FileIcon = ({ node }: { node: NodeApi<FileNode> }) => {
}; };
// Define a Node component that matches what React-Arborist expects // Define a Node component that matches what React-Arborist expects
function Node(props: any) { function Node(props: {
node: NodeApi<FileNode>;
style: React.CSSProperties;
dragHandle: React.Ref<HTMLDivElement>;
}) {
const { node, style, dragHandle } = props; const { node, style, dragHandle } = props;
const handleClick = () => { const handleClick = () => {
@@ -119,7 +122,7 @@ const FileTree: React.FC<FileTreeProps> = ({
onActivate={(node) => { onActivate={(node) => {
const fileNode = node.data as FileNode; const fileNode = node.data as FileNode;
if (!node.isInternal) { if (!node.isInternal) {
handleFileSelect(fileNode.path); void handleFileSelect(fileNode.path);
} }
}} }}
{...({ {...({
@@ -127,7 +130,7 @@ const FileTree: React.FC<FileTreeProps> = ({
onNodeClick: (node: NodeApi<FileNode>) => { onNodeClick: (node: NodeApi<FileNode>) => {
const fileNode = node.data; const fileNode = node.data;
if (!node.isInternal) { if (!node.isInternal) {
handleFileSelect(fileNode.path); void handleFileSelect(fileNode.path);
} }
}, },
} as any)} } as any)}