diff --git a/dev-dist/sw.js b/dev-dist/sw.js index 278d092..5451ed4 100644 --- a/dev-dist/sw.js +++ b/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "/index.html", - "revision": "0.6dag44kodlo" + "revision": "0.ri1maclacqo" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), { diff --git a/src/api/notesApi.ts b/src/api/notesApi.ts index 93ff961..72c021c 100644 --- a/src/api/notesApi.ts +++ b/src/api/notesApi.ts @@ -43,7 +43,7 @@ export const notesApi = { return data; }, - unarchive: async (id: number) => { + unarchive: async (id: number | string) => { const { data } = await axiosClient.put(`/notes/${id}/unarchive`); return data; }, @@ -89,7 +89,7 @@ export const notesApi = { return data; }, - deleteArchived: async (id: number) => { + deleteArchived: async (id: number | string) => { await axiosClient.delete(`/notes/archived/${id}`); }, diff --git a/src/api/userApi.ts b/src/api/userApi.ts index 3655742..06ffd33 100644 --- a/src/api/userApi.ts +++ b/src/api/userApi.ts @@ -12,8 +12,8 @@ export const userApi = { currentPassword?: string; newPassword?: string; accent_color?: string; - show_edit_date?: boolean; - colored_icons?: boolean; + show_edit_date?: boolean | number; + colored_icons?: boolean | number; } ) => { const { data } = await axiosClient.put("/user/profile", profile); 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..38003b6 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; + action?: () => void; + before?: string; + after?: string; + title?: string; + icon?: string; + }> = []; return (
= ({ }} title={btn.title} > - + {btn.icon && } ))} diff --git a/src/components/notes/NoteEditor.tsx b/src/components/notes/NoteEditor.tsx index f59d1f7..78fec79 100644 --- a/src/components/notes/NoteEditor.tsx +++ b/src/components/notes/NoteEditor.tsx @@ -4,7 +4,7 @@ import { FloatingToolbar } from "./FloatingToolbar"; import { NotePreview } from "./NotePreview"; import { ImageUpload } from "./ImageUpload"; import { FileUpload } from "./FileUpload"; -import { useAppSelector, useAppDispatch } from "../../store/hooks"; +import { useAppSelector } from "../../store/hooks"; import { useNotification } from "../../hooks/useNotification"; import { offlineNotesApi } from "../../api/offlineNotesApi"; import { aiApi } from "../../api/aiApi"; @@ -31,7 +31,6 @@ export const NoteEditor: React.FC = ({ 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..9eeb4f2 100644 --- a/src/components/notes/NoteItem.tsx +++ b/src/components/notes/NoteItem.tsx @@ -31,7 +31,7 @@ interface NoteItemProps { export const NoteItem: React.FC = ({ note, - onDelete, + onDelete: _onDelete, onPin, onArchive, onReload, @@ -41,8 +41,8 @@ export const NoteItem: React.FC = ({ const [showArchiveModal, setShowArchiveModal] = useState(false); const [editImages, setEditImages] = useState([]); const [editFiles, setEditFiles] = useState([]); - const [deletedImageIds, setDeletedImageIds] = useState([]); - const [deletedFileIds, setDeletedFileIds] = useState([]); + const [deletedImageIds, setDeletedImageIds] = useState<(number | string)[]>([]); + const [deletedFileIds, setDeletedFileIds] = useState<(number | string)[]>([]); const [isAiLoading, setIsAiLoading] = useState(false); const [showFloatingToolbar, setShowFloatingToolbar] = useState(false); const [toolbarPosition, setToolbarPosition] = useState({ top: 0, left: 0 }); @@ -140,19 +140,19 @@ export const NoteItem: React.FC = ({ setLocalPreviewMode(false); }; - const handleDeleteExistingImage = (imageId: number) => { + const handleDeleteExistingImage = (imageId: number | string) => { setDeletedImageIds([...deletedImageIds, imageId]); }; - const handleDeleteExistingFile = (fileId: number) => { + const handleDeleteExistingFile = (fileId: number | string) => { setDeletedFileIds([...deletedFileIds, fileId]); }; - const handleRestoreImage = (imageId: number) => { + const handleRestoreImage = (imageId: number | string) => { setDeletedImageIds(deletedImageIds.filter((id) => id !== imageId)); }; - const handleRestoreFile = (fileId: number) => { + const handleRestoreFile = (fileId: number | string) => { setDeletedFileIds(deletedFileIds.filter((id) => id !== fileId)); }; @@ -337,36 +337,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 +619,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 +741,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; } @@ -1386,11 +1374,6 @@ export const NoteItem: React.FC = ({ {note.files .filter((file) => !deletedFileIds.includes(file.id)) .map((file) => { - const fileUrl = getFileUrl( - file.file_path, - note.id, - file.id - ); 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..2e455bc 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 id = `notification-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + dispatch(addNotification({ id, message, type })); 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..5d35bcb 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -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"; @@ -26,7 +25,6 @@ const SettingsPage: React.FC = () => { 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 при инициализации @@ -164,8 +162,8 @@ const SettingsPage: React.FC = () => { try { await userApi.updateProfile({ accent_color: selectedAccentColor, - show_edit_date: showEditDate, - colored_icons: coloredIcons, + show_edit_date: showEditDate ? 1 : 0, + colored_icons: coloredIcons ? 1 : 0, }); dispatch(setAccentColorAction(selectedAccentColor)); setAccentColor(selectedAccentColor); @@ -273,7 +271,7 @@ const SettingsPage: React.FC = () => { } }; - const handleRestoreNote = async (id: number) => { + const handleRestoreNote = async (id: number | string) => { try { await notesApi.unarchive(id); await loadArchivedNotes(); @@ -287,7 +285,7 @@ const SettingsPage: React.FC = () => { } }; - const handleDeletePermanent = async (id: number) => { + const handleDeletePermanent = async (id: number | string) => { try { await notesApi.deleteArchived(id); await loadArchivedNotes(); @@ -420,8 +418,8 @@ const SettingsPage: React.FC = () => { // Загружаем версию из IndexedDB try { - const userId = user?.id; - const localVer = userId + const userId = (user as any)?.id; + const localVer = userId && typeof userId === 'number' ? await dbManager.getDataVersionByUserId(userId) : await dbManager.getDataVersion(); setIndexedDBVersion(localVer); diff --git a/src/services/syncService.ts b/src/services/syncService.ts index 45e023c..c08f2f7 100644 --- a/src/services/syncService.ts +++ b/src/services/syncService.ts @@ -5,13 +5,11 @@ import { Note, NoteImage, NoteFile } from '../types/note'; import { store } from '../store/index'; import { setSyncStatus, - removeNotification, addNotification, } from '../store/slices/uiSlice'; import { updateNote, setPendingSyncCount, - setOfflineMode, } from '../store/slices/notesSlice'; import { SyncQueueItem } from '../types/note'; @@ -20,7 +18,7 @@ const RETRY_DELAY_MS = 5000; class SyncService { private isSyncing = false; - private syncTimer: NodeJS.Timeout | null = null; + private syncTimer: ReturnType | null = null; private listeners: Array<() => void> = []; /** @@ -444,7 +442,7 @@ class SyncService { */ private async updateImageReferences( localNote: Note, - serverNote: Note + _serverNote: Note ): Promise { // Если нет изображений с base64, возвращаем как есть const hasBase64Images = localNote.images.some((img) => img.base64Data); @@ -461,7 +459,7 @@ class SyncService { */ private async updateFileReferences( localNote: Note, - serverNote: Note + _serverNote: Note ): Promise { // Если нет файлов с base64, возвращаем как есть const hasBase64Files = localNote.files.some((file) => file.base64Data); diff --git a/src/store/slices/uiSlice.ts b/src/store/slices/uiSlice.ts index 980460b..d7b1d40 100644 --- a/src/store/slices/uiSlice.ts +++ b/src/store/slices/uiSlice.ts @@ -50,9 +50,11 @@ const uiSlice = createSlice({ }, addNotification: ( state, - action: PayloadAction> + action: PayloadAction | Notification> ) => { - const id = `notification-${Date.now()}-${Math.random() + const id = ('id' in action.payload && action.payload.id) + ? action.payload.id + : `notification-${Date.now()}-${Math.random() .toString(36) .substr(2, 9)}`; state.notifications.push({ ...action.payload, id }); diff --git a/src/utils/filePaths.ts b/src/utils/filePaths.ts index 40b34c7..9962a4a 100644 --- a/src/utils/filePaths.ts +++ b/src/utils/filePaths.ts @@ -11,8 +11,8 @@ */ export function getImageUrl( filePath: string, - noteId: number, - imageId: number + noteId: number | string, + imageId: number | string ): string { // Если путь уже является полным URL (начинается с http:// или https://) if (filePath.startsWith("http://") || filePath.startsWith("https://")) { @@ -47,8 +47,8 @@ export function getImageUrl( */ export function getFileUrl( filePath: string, - noteId: number, - fileId: number + noteId: number | string, + fileId: number | string ): string { // Если путь уже является полным URL (начинается с http:// или https://) if (filePath.startsWith("http://") || filePath.startsWith("https://")) { diff --git a/src/utils/offlineManager.ts b/src/utils/offlineManager.ts index ec9aa8a..df5025c 100644 --- a/src/utils/offlineManager.ts +++ b/src/utils/offlineManager.ts @@ -55,7 +55,7 @@ export async function checkNetworkStatus(): Promise { const timeoutId = setTimeout(() => controller.abort(), 1500); // Используем /auth/status так как он всегда доступен при наличии сети - const response = await fetch('/api/auth/status', { + await fetch('/api/auth/status', { method: 'GET', signal: controller.signal, cache: 'no-cache',