From 4d91b2227d38081b8eb6a5e043502157901caa00 Mon Sep 17 00:00:00 2001 From: Fovway Date: Tue, 4 Nov 2025 11:51:32 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BF=D1=80=D0=BE=D1=89=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B0=20=D1=82=D0=B8?= =?UTF-8?q?=D0=BF=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=B0=D1=85.?= =?UTF-8?q?=20=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B5?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D1=83=D0=B5=D0=BC?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8=20=D0=B8=20=D0=BE=D0=BF=D1=82=D0=B8=D0=BC?= =?UTF-8?q?=D0=B8=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B8=20=D0=B8=D0=B4=D0=B5=D0=BD=D1=82=D0=B8?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D0=B0=D1=82=D0=BE=D1=80=D0=BE=D0=B2=20=D0=B2?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D1=85=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=82=D0=BE=D0=BA.=20=D0=9E?= =?UTF-8?q?=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BB=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B8?= =?UTF-8?q?=D0=B7=D0=B2=D0=BE=D0=B4=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=B8=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=BE=D0=BF=D1=8B=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/calendar/MiniCalendar.tsx | 2 +- src/components/layout/Sidebar.tsx | 1 - src/components/notes/MarkdownToolbar.tsx | 9 ++- src/components/notes/NoteEditor.tsx | 23 ++----- src/components/notes/NoteItem.tsx | 88 +++++++++++++----------- src/components/notes/NotesList.tsx | 4 +- src/components/search/SearchBar.tsx | 2 +- src/hooks/useNotification.ts | 3 +- src/pages/ProfilePage.tsx | 3 +- src/pages/SettingsPage.tsx | 24 ++++--- src/services/syncService.ts | 8 +-- 11 files changed, 83 insertions(+), 84 deletions(-) diff --git a/src/components/calendar/MiniCalendar.tsx b/src/components/calendar/MiniCalendar.tsx index df75a5c..dade509 100644 --- a/src/components/calendar/MiniCalendar.tsx +++ b/src/components/calendar/MiniCalendar.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState } from "react"; import { format, startOfMonth, diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index ab78c39..db6b787 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -2,7 +2,6 @@ import React from "react"; import { MiniCalendar } from "../calendar/MiniCalendar"; import { SearchBar } from "../search/SearchBar"; import { TagsFilter } from "../search/TagsFilter"; -import { useAppSelector } from "../../store/hooks"; import { Note } from "../../types/note"; interface SidebarProps { diff --git a/src/components/notes/MarkdownToolbar.tsx b/src/components/notes/MarkdownToolbar.tsx index e6d7817..a67fd65 100644 --- a/src/components/notes/MarkdownToolbar.tsx +++ b/src/components/notes/MarkdownToolbar.tsx @@ -113,7 +113,14 @@ export const MarkdownToolbar: React.FC = ({ }; }, [isDragging]); - const buttons = []; + const buttons: Array<{ + id: string; + icon: string; + title: string; + before?: string; + after?: string; + action?: () => void; + }> = []; return (
= ({ onSave }) => { const isPreviewMode = useAppSelector((state) => state.ui.isPreviewMode); const { showNotification } = useNotification(); const aiEnabled = useAppSelector((state) => state.profile.aiEnabled); - const dispatch = useAppDispatch(); const handleSave = async () => { if (!content.trim()) { @@ -251,36 +250,27 @@ export const NoteEditor: React.FC = ({ onSave }) => { // Определяем, есть ли уже такие маркеры на всех строках let allLinesHaveMarker = true; - let hasAnyMarker = false; for (const line of lines) { const trimmedLine = line.trimStart(); if (before === "- ") { // Для маркированного списка проверяем различные варианты - if (trimmedLine.match(/^[-*+]\s/)) { - hasAnyMarker = true; - } else { + if (!trimmedLine.match(/^[-*+]\s/)) { allLinesHaveMarker = false; } } else if (before === "1. ") { // Для нумерованного списка - if (trimmedLine.match(/^\d+\.\s/)) { - hasAnyMarker = true; - } else { + if (!trimmedLine.match(/^\d+\.\s/)) { allLinesHaveMarker = false; } } else if (before === "- [ ] ") { // Для чекбокса - if (trimmedLine.match(/^-\s+\[[ xX]\]\s/)) { - hasAnyMarker = true; - } else { + if (!trimmedLine.match(/^-\s+\[[ xX]\]\s/)) { allLinesHaveMarker = false; } } else if (before === "> ") { // Для цитаты - if (trimmedLine.startsWith("> ")) { - hasAnyMarker = true; - } else { + if (!trimmedLine.startsWith("> ")) { allLinesHaveMarker = false; } } @@ -530,14 +520,12 @@ export const NoteEditor: React.FC = ({ onSave }) => { const lines = text.split("\n"); // Определяем текущую строку - let currentLineIndex = 0; let currentLineStart = 0; let currentLine = ""; for (let i = 0; i < lines.length; i++) { const lineLength = lines[i].length; if (currentLineStart + lineLength >= start) { - currentLineIndex = i; currentLine = lines[i]; break; } @@ -668,7 +656,6 @@ export const NoteEditor: React.FC = ({ onSave }) => { const lineHeight = parseInt(styles.lineHeight) || 20; const paddingTop = parseInt(styles.paddingTop) || 0; const paddingLeft = parseInt(styles.paddingLeft) || 0; - const fontSize = parseInt(styles.fontSize) || 14; // Более точный расчет ширины символа // Создаем временный элемент для измерения diff --git a/src/components/notes/NoteItem.tsx b/src/components/notes/NoteItem.tsx index 233691e..b8df60a 100644 --- a/src/components/notes/NoteItem.tsx +++ b/src/components/notes/NoteItem.tsx @@ -31,7 +31,6 @@ interface NoteItemProps { export const NoteItem: React.FC = ({ note, - onDelete, onPin, onArchive, onReload, @@ -140,20 +139,24 @@ export const NoteItem: React.FC = ({ setLocalPreviewMode(false); }; - const handleDeleteExistingImage = (imageId: number) => { - setDeletedImageIds([...deletedImageIds, imageId]); + const handleDeleteExistingImage = (imageId: number | string) => { + const id = typeof imageId === 'number' ? imageId : Number(imageId); + setDeletedImageIds([...deletedImageIds, id]); }; - const handleDeleteExistingFile = (fileId: number) => { - setDeletedFileIds([...deletedFileIds, fileId]); + const handleDeleteExistingFile = (fileId: number | string) => { + const id = typeof fileId === 'number' ? fileId : Number(fileId); + setDeletedFileIds([...deletedFileIds, id]); }; - const handleRestoreImage = (imageId: number) => { - setDeletedImageIds(deletedImageIds.filter((id) => id !== imageId)); + const handleRestoreImage = (imageId: number | string) => { + const id = typeof imageId === 'number' ? imageId : Number(imageId); + setDeletedImageIds(deletedImageIds.filter((deletedId) => deletedId !== id)); }; - const handleRestoreFile = (fileId: number) => { - setDeletedFileIds(deletedFileIds.filter((id) => id !== fileId)); + const handleRestoreFile = (fileId: number | string) => { + const id = typeof fileId === 'number' ? fileId : Number(fileId); + setDeletedFileIds(deletedFileIds.filter((deletedId) => deletedId !== id)); }; const handleAiImprove = async () => { @@ -337,36 +340,27 @@ export const NoteItem: React.FC = ({ // Определяем, есть ли уже такие маркеры на всех строках let allLinesHaveMarker = true; - let hasAnyMarker = false; for (const line of lines) { const trimmedLine = line.trimStart(); if (before === "- ") { // Для маркированного списка проверяем различные варианты - if (trimmedLine.match(/^[-*+]\s/)) { - hasAnyMarker = true; - } else { + if (!trimmedLine.match(/^[-*+]\s/)) { allLinesHaveMarker = false; } } else if (before === "1. ") { // Для нумерованного списка - if (trimmedLine.match(/^\d+\.\s/)) { - hasAnyMarker = true; - } else { + if (!trimmedLine.match(/^\d+\.\s/)) { allLinesHaveMarker = false; } } else if (before === "- [ ] ") { // Для чекбокса - if (trimmedLine.match(/^-\s+\[[ xX]\]\s/)) { - hasAnyMarker = true; - } else { + if (!trimmedLine.match(/^-\s+\[[ xX]\]\s/)) { allLinesHaveMarker = false; } } else if (before === "> ") { // Для цитаты - if (trimmedLine.startsWith("> ")) { - hasAnyMarker = true; - } else { + if (!trimmedLine.startsWith("> ")) { allLinesHaveMarker = false; } } @@ -628,7 +622,6 @@ export const NoteItem: React.FC = ({ const lineHeight = parseInt(styles.lineHeight) || 20; const paddingTop = parseInt(styles.paddingTop) || 0; const paddingLeft = parseInt(styles.paddingLeft) || 0; - const fontSize = parseInt(styles.fontSize) || 14; // Более точный расчет ширины символа // Создаем временный элемент для измерения @@ -751,14 +744,12 @@ export const NoteItem: React.FC = ({ const lines = text.split("\n"); // Определяем текущую строку - let currentLineIndex = 0; let currentLineStart = 0; let currentLine = ""; for (let i = 0; i < lines.length; i++) { const lineLength = lines[i].length; if (currentLineStart + lineLength >= start) { - currentLineIndex = i; currentLine = lines[i]; break; } @@ -1305,12 +1296,17 @@ export const NoteItem: React.FC = ({
{note.images - .filter((image) => !deletedImageIds.includes(image.id)) + .filter((image) => { + const imageId = typeof image.id === 'number' ? image.id : Number(image.id); + return !deletedImageIds.includes(imageId); + }) .map((image) => { + const noteId = typeof note.id === 'number' ? note.id : Number(note.id); + const imageId = typeof image.id === 'number' ? image.id : Number(image.id); const imageUrl = getImageUrl( image.file_path, - note.id, - image.id + noteId, + imageId ); return (
@@ -1344,12 +1340,17 @@ export const NoteItem: React.FC = ({
{note.images - .filter((image) => deletedImageIds.includes(image.id)) + .filter((image) => { + const imageId = typeof image.id === 'number' ? image.id : Number(image.id); + return deletedImageIds.includes(imageId); + }) .map((image) => { + const noteId = typeof note.id === 'number' ? note.id : Number(note.id); + const imageId = typeof image.id === 'number' ? image.id : Number(image.id); const imageUrl = getImageUrl( image.file_path, - note.id, - image.id + noteId, + imageId ); return (
@@ -1384,13 +1385,11 @@ export const NoteItem: React.FC = ({
{note.files - .filter((file) => !deletedFileIds.includes(file.id)) + .filter((file) => { + const fileId = typeof file.id === 'number' ? file.id : Number(file.id); + return !deletedFileIds.includes(fileId); + }) .map((file) => { - const fileUrl = getFileUrl( - file.file_path, - note.id, - file.id - ); return (
= ({
{note.files - .filter((file) => deletedFileIds.includes(file.id)) + .filter((file) => { + const fileId = typeof file.id === 'number' ? file.id : Number(file.id); + return deletedFileIds.includes(fileId); + }) .map((file) => { return (
@@ -1524,10 +1526,12 @@ export const NoteItem: React.FC = ({ {note.images && note.images.length > 0 && (
{note.images.map((image) => { + const noteId = typeof note.id === 'number' ? note.id : Number(note.id); + const imageId = typeof image.id === 'number' ? image.id : Number(image.id); const imageUrl = getImageUrl( image.file_path, - note.id, - image.id + noteId, + imageId ); return (
@@ -1549,7 +1553,9 @@ export const NoteItem: React.FC = ({ {note.files && note.files.length > 0 && (
{note.files.map((file) => { - const fileUrl = getFileUrl(file.file_path, note.id, file.id); + const noteId = typeof note.id === 'number' ? note.id : Number(note.id); + const fileId = typeof file.id === 'number' ? file.id : Number(file.id); + const fileUrl = getFileUrl(file.file_path, noteId, fileId); return (
void; } -export const NotesList = forwardRef((props, ref) => { +export const NotesList = forwardRef((_props, ref) => { const notes = useAppSelector((state) => state.notes.notes); const userId = useAppSelector((state) => state.auth.userId); const searchQuery = useAppSelector((state) => state.notes.searchQuery); diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx index 25d067e..f5d6960 100644 --- a/src/components/search/SearchBar.tsx +++ b/src/components/search/SearchBar.tsx @@ -6,7 +6,7 @@ import { setSearchQuery } from "../../store/slices/notesSlice"; export const SearchBar: React.FC = () => { const [query, setQuery] = useState(""); const dispatch = useAppDispatch(); - const timeoutRef = useRef(null); + const timeoutRef = useRef | null>(null); useEffect(() => { // Debounce для поиска diff --git a/src/hooks/useNotification.ts b/src/hooks/useNotification.ts index f7e8422..5379a85 100644 --- a/src/hooks/useNotification.ts +++ b/src/hooks/useNotification.ts @@ -10,7 +10,8 @@ export const useNotification = () => { message: string, type: "info" | "success" | "error" | "warning" = "info" ) => { - const id = dispatch(addNotification({ message, type })).payload.id; + const action = dispatch(addNotification({ message, type })); + const id = (action as any).payload?.id || `notification-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; setTimeout(() => { dispatch(removeNotification(id)); }, 4000); diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 8a87862..970a44a 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { Icon } from "@iconify/react"; -import { useAppSelector, useAppDispatch } from "../store/hooks"; +import { useAppDispatch } from "../store/hooks"; import { userApi } from "../api/userApi"; import { authApi } from "../api/authApi"; import { clearAuth } from "../store/slices/authSlice"; @@ -16,7 +16,6 @@ const ProfilePage: React.FC = () => { const navigate = useNavigate(); const dispatch = useAppDispatch(); const { showNotification } = useNotification(); - const user = useAppSelector((state) => state.profile.user); const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 157b8f2..2a407b7 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useCallback } from "react"; import { useNavigate } from "react-router-dom"; import { Icon } from "@iconify/react"; -import { useAppSelector, useAppDispatch } from "../store/hooks"; +import { useAppDispatch } from "../store/hooks"; import { userApi } from "../api/userApi"; import { authApi } from "../api/authApi"; import { notesApi, logsApi, Log } from "../api/notesApi"; @@ -13,7 +13,6 @@ import { setAccentColor } from "../utils/colorUtils"; import { useNotification } from "../hooks/useNotification"; import { Modal } from "../components/common/Modal"; import { ThemeToggle } from "../components/common/ThemeToggle"; -import { formatDateFromTimestamp } from "../utils/dateFormat"; import { parseMarkdown } from "../utils/markdown"; import { dbManager } from "../utils/indexedDB"; import { syncService } from "../services/syncService"; @@ -25,8 +24,6 @@ const SettingsPage: React.FC = () => { const navigate = useNavigate(); const dispatch = useAppDispatch(); const { showNotification } = useNotification(); - const user = useAppSelector((state) => state.profile.user); - const accentColor = useAppSelector((state) => state.ui.accentColor); const [activeTab, setActiveTab] = useState(() => { // Восстанавливаем активную вкладку из localStorage при инициализации @@ -162,11 +159,16 @@ const SettingsPage: React.FC = () => { const handleUpdateAppearance = async () => { try { - await userApi.updateProfile({ + const profileUpdate: any = { accent_color: selectedAccentColor, - show_edit_date: showEditDate, - colored_icons: coloredIcons, - }); + }; + if (showEditDate !== undefined) { + profileUpdate.show_edit_date = showEditDate; + } + if (coloredIcons !== undefined) { + profileUpdate.colored_icons = coloredIcons; + } + await userApi.updateProfile(profileUpdate); dispatch(setAccentColorAction(selectedAccentColor)); setAccentColor(selectedAccentColor); await loadUserInfo(); @@ -420,7 +422,7 @@ const SettingsPage: React.FC = () => { // Загружаем версию из IndexedDB try { - const userId = user?.id; + const userId = (await import("../store")).store.getState().auth.userId; const localVer = userId ? await dbManager.getDataVersionByUserId(userId) : await dbManager.getDataVersion(); @@ -837,14 +839,14 @@ const SettingsPage: React.FC = () => {