Обновлены API для работы с заметками и пользователями, добавлена поддержка идентификаторов в виде строк. Изменены параметры в компонентах для улучшения обработки данных. Оптимизированы стили и логика компонентов для повышения производительности и улучшения пользовательского опыта.

This commit is contained in:
Fovway 2025-11-04 11:19:46 +07:00
parent 4b3bc3e024
commit 5b3e41d1b6
17 changed files with 55 additions and 81 deletions

View File

@ -82,7 +82,7 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "/index.html", "url": "/index.html",
"revision": "0.6dag44kodlo" "revision": "0.ri1maclacqo"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), {

View File

@ -43,7 +43,7 @@ export const notesApi = {
return data; return data;
}, },
unarchive: async (id: number) => { unarchive: async (id: number | string) => {
const { data } = await axiosClient.put(`/notes/${id}/unarchive`); const { data } = await axiosClient.put(`/notes/${id}/unarchive`);
return data; return data;
}, },
@ -89,7 +89,7 @@ export const notesApi = {
return data; return data;
}, },
deleteArchived: async (id: number) => { deleteArchived: async (id: number | string) => {
await axiosClient.delete(`/notes/archived/${id}`); await axiosClient.delete(`/notes/archived/${id}`);
}, },

View File

@ -12,8 +12,8 @@ export const userApi = {
currentPassword?: string; currentPassword?: string;
newPassword?: string; newPassword?: string;
accent_color?: string; accent_color?: string;
show_edit_date?: boolean; show_edit_date?: boolean | number;
colored_icons?: boolean; colored_icons?: boolean | number;
} }
) => { ) => {
const { data } = await axiosClient.put("/user/profile", profile); const { data } = await axiosClient.put("/user/profile", profile);

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState } from "react";
import { import {
format, format,
startOfMonth, startOfMonth,

View File

@ -2,7 +2,6 @@ import React from "react";
import { MiniCalendar } from "../calendar/MiniCalendar"; import { MiniCalendar } from "../calendar/MiniCalendar";
import { SearchBar } from "../search/SearchBar"; import { SearchBar } from "../search/SearchBar";
import { TagsFilter } from "../search/TagsFilter"; import { TagsFilter } from "../search/TagsFilter";
import { useAppSelector } from "../../store/hooks";
import { Note } from "../../types/note"; import { Note } from "../../types/note";
interface SidebarProps { interface SidebarProps {

View File

@ -113,7 +113,14 @@ export const MarkdownToolbar: React.FC<MarkdownToolbarProps> = ({
}; };
}, [isDragging]); }, [isDragging]);
const buttons = []; const buttons: Array<{
id: string;
action?: () => void;
before?: string;
after?: string;
title?: string;
icon?: string;
}> = [];
return ( return (
<div <div
@ -142,7 +149,7 @@ export const MarkdownToolbar: React.FC<MarkdownToolbarProps> = ({
}} }}
title={btn.title} title={btn.title}
> >
<Icon icon={btn.icon} /> {btn.icon && <Icon icon={btn.icon} />}
</button> </button>
))} ))}

View File

@ -4,7 +4,7 @@ import { FloatingToolbar } from "./FloatingToolbar";
import { NotePreview } from "./NotePreview"; import { NotePreview } from "./NotePreview";
import { ImageUpload } from "./ImageUpload"; import { ImageUpload } from "./ImageUpload";
import { FileUpload } from "./FileUpload"; import { FileUpload } from "./FileUpload";
import { useAppSelector, useAppDispatch } from "../../store/hooks"; import { useAppSelector } from "../../store/hooks";
import { useNotification } from "../../hooks/useNotification"; import { useNotification } from "../../hooks/useNotification";
import { offlineNotesApi } from "../../api/offlineNotesApi"; import { offlineNotesApi } from "../../api/offlineNotesApi";
import { aiApi } from "../../api/aiApi"; import { aiApi } from "../../api/aiApi";
@ -31,7 +31,6 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
const isPreviewMode = useAppSelector((state) => state.ui.isPreviewMode); const isPreviewMode = useAppSelector((state) => state.ui.isPreviewMode);
const { showNotification } = useNotification(); const { showNotification } = useNotification();
const aiEnabled = useAppSelector((state) => state.profile.aiEnabled); const aiEnabled = useAppSelector((state) => state.profile.aiEnabled);
const dispatch = useAppDispatch();
const handleSave = async () => { const handleSave = async () => {
if (!content.trim()) { if (!content.trim()) {
@ -251,36 +250,27 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
// Определяем, есть ли уже такие маркеры на всех строках // Определяем, есть ли уже такие маркеры на всех строках
let allLinesHaveMarker = true; let allLinesHaveMarker = true;
let hasAnyMarker = false;
for (const line of lines) { for (const line of lines) {
const trimmedLine = line.trimStart(); const trimmedLine = line.trimStart();
if (before === "- ") { if (before === "- ") {
// Для маркированного списка проверяем различные варианты // Для маркированного списка проверяем различные варианты
if (trimmedLine.match(/^[-*+]\s/)) { if (!trimmedLine.match(/^[-*+]\s/)) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} else if (before === "1. ") { } else if (before === "1. ") {
// Для нумерованного списка // Для нумерованного списка
if (trimmedLine.match(/^\d+\.\s/)) { if (!trimmedLine.match(/^\d+\.\s/)) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} else if (before === "- [ ] ") { } else if (before === "- [ ] ") {
// Для чекбокса // Для чекбокса
if (trimmedLine.match(/^-\s+\[[ xX]\]\s/)) { if (!trimmedLine.match(/^-\s+\[[ xX]\]\s/)) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} else if (before === "> ") { } else if (before === "> ") {
// Для цитаты // Для цитаты
if (trimmedLine.startsWith("> ")) { if (!trimmedLine.startsWith("> ")) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} }
@ -530,14 +520,12 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
const lines = text.split("\n"); const lines = text.split("\n");
// Определяем текущую строку // Определяем текущую строку
let currentLineIndex = 0;
let currentLineStart = 0; let currentLineStart = 0;
let currentLine = ""; let currentLine = "";
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
const lineLength = lines[i].length; const lineLength = lines[i].length;
if (currentLineStart + lineLength >= start) { if (currentLineStart + lineLength >= start) {
currentLineIndex = i;
currentLine = lines[i]; currentLine = lines[i];
break; break;
} }
@ -668,7 +656,6 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
const lineHeight = parseInt(styles.lineHeight) || 20; const lineHeight = parseInt(styles.lineHeight) || 20;
const paddingTop = parseInt(styles.paddingTop) || 0; const paddingTop = parseInt(styles.paddingTop) || 0;
const paddingLeft = parseInt(styles.paddingLeft) || 0; const paddingLeft = parseInt(styles.paddingLeft) || 0;
const fontSize = parseInt(styles.fontSize) || 14;
// Более точный расчет ширины символа // Более точный расчет ширины символа
// Создаем временный элемент для измерения // Создаем временный элемент для измерения

View File

@ -31,7 +31,7 @@ interface NoteItemProps {
export const NoteItem: React.FC<NoteItemProps> = ({ export const NoteItem: React.FC<NoteItemProps> = ({
note, note,
onDelete, onDelete: _onDelete,
onPin, onPin,
onArchive, onArchive,
onReload, onReload,
@ -41,8 +41,8 @@ export const NoteItem: React.FC<NoteItemProps> = ({
const [showArchiveModal, setShowArchiveModal] = useState(false); const [showArchiveModal, setShowArchiveModal] = useState(false);
const [editImages, setEditImages] = useState<File[]>([]); const [editImages, setEditImages] = useState<File[]>([]);
const [editFiles, setEditFiles] = useState<File[]>([]); const [editFiles, setEditFiles] = useState<File[]>([]);
const [deletedImageIds, setDeletedImageIds] = useState<number[]>([]); const [deletedImageIds, setDeletedImageIds] = useState<(number | string)[]>([]);
const [deletedFileIds, setDeletedFileIds] = useState<number[]>([]); const [deletedFileIds, setDeletedFileIds] = useState<(number | string)[]>([]);
const [isAiLoading, setIsAiLoading] = useState(false); const [isAiLoading, setIsAiLoading] = useState(false);
const [showFloatingToolbar, setShowFloatingToolbar] = useState(false); const [showFloatingToolbar, setShowFloatingToolbar] = useState(false);
const [toolbarPosition, setToolbarPosition] = useState({ top: 0, left: 0 }); const [toolbarPosition, setToolbarPosition] = useState({ top: 0, left: 0 });
@ -140,19 +140,19 @@ export const NoteItem: React.FC<NoteItemProps> = ({
setLocalPreviewMode(false); setLocalPreviewMode(false);
}; };
const handleDeleteExistingImage = (imageId: number) => { const handleDeleteExistingImage = (imageId: number | string) => {
setDeletedImageIds([...deletedImageIds, imageId]); setDeletedImageIds([...deletedImageIds, imageId]);
}; };
const handleDeleteExistingFile = (fileId: number) => { const handleDeleteExistingFile = (fileId: number | string) => {
setDeletedFileIds([...deletedFileIds, fileId]); setDeletedFileIds([...deletedFileIds, fileId]);
}; };
const handleRestoreImage = (imageId: number) => { const handleRestoreImage = (imageId: number | string) => {
setDeletedImageIds(deletedImageIds.filter((id) => id !== imageId)); setDeletedImageIds(deletedImageIds.filter((id) => id !== imageId));
}; };
const handleRestoreFile = (fileId: number) => { const handleRestoreFile = (fileId: number | string) => {
setDeletedFileIds(deletedFileIds.filter((id) => id !== fileId)); setDeletedFileIds(deletedFileIds.filter((id) => id !== fileId));
}; };
@ -337,36 +337,27 @@ export const NoteItem: React.FC<NoteItemProps> = ({
// Определяем, есть ли уже такие маркеры на всех строках // Определяем, есть ли уже такие маркеры на всех строках
let allLinesHaveMarker = true; let allLinesHaveMarker = true;
let hasAnyMarker = false;
for (const line of lines) { for (const line of lines) {
const trimmedLine = line.trimStart(); const trimmedLine = line.trimStart();
if (before === "- ") { if (before === "- ") {
// Для маркированного списка проверяем различные варианты // Для маркированного списка проверяем различные варианты
if (trimmedLine.match(/^[-*+]\s/)) { if (!trimmedLine.match(/^[-*+]\s/)) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} else if (before === "1. ") { } else if (before === "1. ") {
// Для нумерованного списка // Для нумерованного списка
if (trimmedLine.match(/^\d+\.\s/)) { if (!trimmedLine.match(/^\d+\.\s/)) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} else if (before === "- [ ] ") { } else if (before === "- [ ] ") {
// Для чекбокса // Для чекбокса
if (trimmedLine.match(/^-\s+\[[ xX]\]\s/)) { if (!trimmedLine.match(/^-\s+\[[ xX]\]\s/)) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} else if (before === "> ") { } else if (before === "> ") {
// Для цитаты // Для цитаты
if (trimmedLine.startsWith("> ")) { if (!trimmedLine.startsWith("> ")) {
hasAnyMarker = true;
} else {
allLinesHaveMarker = false; allLinesHaveMarker = false;
} }
} }
@ -628,7 +619,6 @@ export const NoteItem: React.FC<NoteItemProps> = ({
const lineHeight = parseInt(styles.lineHeight) || 20; const lineHeight = parseInt(styles.lineHeight) || 20;
const paddingTop = parseInt(styles.paddingTop) || 0; const paddingTop = parseInt(styles.paddingTop) || 0;
const paddingLeft = parseInt(styles.paddingLeft) || 0; const paddingLeft = parseInt(styles.paddingLeft) || 0;
const fontSize = parseInt(styles.fontSize) || 14;
// Более точный расчет ширины символа // Более точный расчет ширины символа
// Создаем временный элемент для измерения // Создаем временный элемент для измерения
@ -751,14 +741,12 @@ export const NoteItem: React.FC<NoteItemProps> = ({
const lines = text.split("\n"); const lines = text.split("\n");
// Определяем текущую строку // Определяем текущую строку
let currentLineIndex = 0;
let currentLineStart = 0; let currentLineStart = 0;
let currentLine = ""; let currentLine = "";
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
const lineLength = lines[i].length; const lineLength = lines[i].length;
if (currentLineStart + lineLength >= start) { if (currentLineStart + lineLength >= start) {
currentLineIndex = i;
currentLine = lines[i]; currentLine = lines[i];
break; break;
} }
@ -1386,11 +1374,6 @@ export const NoteItem: React.FC<NoteItemProps> = ({
{note.files {note.files
.filter((file) => !deletedFileIds.includes(file.id)) .filter((file) => !deletedFileIds.includes(file.id))
.map((file) => { .map((file) => {
const fileUrl = getFileUrl(
file.file_path,
note.id,
file.id
);
return ( return (
<div key={file.id} className="file-preview-item"> <div key={file.id} className="file-preview-item">
<Icon <Icon

View File

@ -1,4 +1,4 @@
import React, { useEffect, useImperativeHandle, forwardRef } from "react"; import { useEffect, useImperativeHandle, forwardRef } from "react";
import { NoteItem } from "./NoteItem"; import { NoteItem } from "./NoteItem";
import { useAppSelector, useAppDispatch } from "../../store/hooks"; import { useAppSelector, useAppDispatch } from "../../store/hooks";
import { offlineNotesApi } from "../../api/offlineNotesApi"; import { offlineNotesApi } from "../../api/offlineNotesApi";
@ -10,7 +10,7 @@ export interface NotesListRef {
reloadNotes: () => void; reloadNotes: () => void;
} }
export const NotesList = forwardRef<NotesListRef>((props, ref) => { export const NotesList = forwardRef<NotesListRef>((_props, ref) => {
const notes = useAppSelector((state) => state.notes.notes); const notes = useAppSelector((state) => state.notes.notes);
const userId = useAppSelector((state) => state.auth.userId); const userId = useAppSelector((state) => state.auth.userId);
const searchQuery = useAppSelector((state) => state.notes.searchQuery); const searchQuery = useAppSelector((state) => state.notes.searchQuery);

View File

@ -6,7 +6,7 @@ import { setSearchQuery } from "../../store/slices/notesSlice";
export const SearchBar: React.FC = () => { export const SearchBar: React.FC = () => {
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const timeoutRef = useRef<NodeJS.Timeout | null>(null); const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => { useEffect(() => {
// Debounce для поиска // Debounce для поиска

View File

@ -10,7 +10,8 @@ export const useNotification = () => {
message: string, message: string,
type: "info" | "success" | "error" | "warning" = "info" 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(() => { setTimeout(() => {
dispatch(removeNotification(id)); dispatch(removeNotification(id));
}, 4000); }, 4000);

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
import { useAppSelector, useAppDispatch } from "../store/hooks"; import { useAppDispatch } from "../store/hooks";
import { userApi } from "../api/userApi"; import { userApi } from "../api/userApi";
import { authApi } from "../api/authApi"; import { authApi } from "../api/authApi";
import { clearAuth } from "../store/slices/authSlice"; import { clearAuth } from "../store/slices/authSlice";
@ -16,7 +16,6 @@ const ProfilePage: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { showNotification } = useNotification(); const { showNotification } = useNotification();
const user = useAppSelector((state) => state.profile.user);
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");

View File

@ -13,7 +13,6 @@ import { setAccentColor } from "../utils/colorUtils";
import { useNotification } from "../hooks/useNotification"; import { useNotification } from "../hooks/useNotification";
import { Modal } from "../components/common/Modal"; import { Modal } from "../components/common/Modal";
import { ThemeToggle } from "../components/common/ThemeToggle"; import { ThemeToggle } from "../components/common/ThemeToggle";
import { formatDateFromTimestamp } from "../utils/dateFormat";
import { parseMarkdown } from "../utils/markdown"; import { parseMarkdown } from "../utils/markdown";
import { dbManager } from "../utils/indexedDB"; import { dbManager } from "../utils/indexedDB";
import { syncService } from "../services/syncService"; import { syncService } from "../services/syncService";
@ -26,7 +25,6 @@ const SettingsPage: React.FC = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { showNotification } = useNotification(); const { showNotification } = useNotification();
const user = useAppSelector((state) => state.profile.user); const user = useAppSelector((state) => state.profile.user);
const accentColor = useAppSelector((state) => state.ui.accentColor);
const [activeTab, setActiveTab] = useState<SettingsTab>(() => { const [activeTab, setActiveTab] = useState<SettingsTab>(() => {
// Восстанавливаем активную вкладку из localStorage при инициализации // Восстанавливаем активную вкладку из localStorage при инициализации
@ -164,8 +162,8 @@ const SettingsPage: React.FC = () => {
try { try {
await userApi.updateProfile({ await userApi.updateProfile({
accent_color: selectedAccentColor, accent_color: selectedAccentColor,
show_edit_date: showEditDate, show_edit_date: showEditDate ? 1 : 0,
colored_icons: coloredIcons, colored_icons: coloredIcons ? 1 : 0,
}); });
dispatch(setAccentColorAction(selectedAccentColor)); dispatch(setAccentColorAction(selectedAccentColor));
setAccentColor(selectedAccentColor); setAccentColor(selectedAccentColor);
@ -273,7 +271,7 @@ const SettingsPage: React.FC = () => {
} }
}; };
const handleRestoreNote = async (id: number) => { const handleRestoreNote = async (id: number | string) => {
try { try {
await notesApi.unarchive(id); await notesApi.unarchive(id);
await loadArchivedNotes(); await loadArchivedNotes();
@ -287,7 +285,7 @@ const SettingsPage: React.FC = () => {
} }
}; };
const handleDeletePermanent = async (id: number) => { const handleDeletePermanent = async (id: number | string) => {
try { try {
await notesApi.deleteArchived(id); await notesApi.deleteArchived(id);
await loadArchivedNotes(); await loadArchivedNotes();
@ -420,8 +418,8 @@ const SettingsPage: React.FC = () => {
// Загружаем версию из IndexedDB // Загружаем версию из IndexedDB
try { try {
const userId = user?.id; const userId = (user as any)?.id;
const localVer = userId const localVer = userId && typeof userId === 'number'
? await dbManager.getDataVersionByUserId(userId) ? await dbManager.getDataVersionByUserId(userId)
: await dbManager.getDataVersion(); : await dbManager.getDataVersion();
setIndexedDBVersion(localVer); setIndexedDBVersion(localVer);

View File

@ -5,13 +5,11 @@ import { Note, NoteImage, NoteFile } from '../types/note';
import { store } from '../store/index'; import { store } from '../store/index';
import { import {
setSyncStatus, setSyncStatus,
removeNotification,
addNotification, addNotification,
} from '../store/slices/uiSlice'; } from '../store/slices/uiSlice';
import { import {
updateNote, updateNote,
setPendingSyncCount, setPendingSyncCount,
setOfflineMode,
} from '../store/slices/notesSlice'; } from '../store/slices/notesSlice';
import { SyncQueueItem } from '../types/note'; import { SyncQueueItem } from '../types/note';
@ -20,7 +18,7 @@ const RETRY_DELAY_MS = 5000;
class SyncService { class SyncService {
private isSyncing = false; private isSyncing = false;
private syncTimer: NodeJS.Timeout | null = null; private syncTimer: ReturnType<typeof setTimeout> | null = null;
private listeners: Array<() => void> = []; private listeners: Array<() => void> = [];
/** /**
@ -444,7 +442,7 @@ class SyncService {
*/ */
private async updateImageReferences( private async updateImageReferences(
localNote: Note, localNote: Note,
serverNote: Note _serverNote: Note
): Promise<NoteImage[]> { ): Promise<NoteImage[]> {
// Если нет изображений с base64, возвращаем как есть // Если нет изображений с base64, возвращаем как есть
const hasBase64Images = localNote.images.some((img) => img.base64Data); const hasBase64Images = localNote.images.some((img) => img.base64Data);
@ -461,7 +459,7 @@ class SyncService {
*/ */
private async updateFileReferences( private async updateFileReferences(
localNote: Note, localNote: Note,
serverNote: Note _serverNote: Note
): Promise<NoteFile[]> { ): Promise<NoteFile[]> {
// Если нет файлов с base64, возвращаем как есть // Если нет файлов с base64, возвращаем как есть
const hasBase64Files = localNote.files.some((file) => file.base64Data); const hasBase64Files = localNote.files.some((file) => file.base64Data);

View File

@ -50,9 +50,11 @@ const uiSlice = createSlice({
}, },
addNotification: ( addNotification: (
state, state,
action: PayloadAction<Omit<Notification, "id">> action: PayloadAction<Omit<Notification, "id"> | 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) .toString(36)
.substr(2, 9)}`; .substr(2, 9)}`;
state.notifications.push({ ...action.payload, id }); state.notifications.push({ ...action.payload, id });

View File

@ -11,8 +11,8 @@
*/ */
export function getImageUrl( export function getImageUrl(
filePath: string, filePath: string,
noteId: number, noteId: number | string,
imageId: number imageId: number | string
): string { ): string {
// Если путь уже является полным URL (начинается с http:// или https://) // Если путь уже является полным URL (начинается с http:// или https://)
if (filePath.startsWith("http://") || filePath.startsWith("https://")) { if (filePath.startsWith("http://") || filePath.startsWith("https://")) {
@ -47,8 +47,8 @@ export function getImageUrl(
*/ */
export function getFileUrl( export function getFileUrl(
filePath: string, filePath: string,
noteId: number, noteId: number | string,
fileId: number fileId: number | string
): string { ): string {
// Если путь уже является полным URL (начинается с http:// или https://) // Если путь уже является полным URL (начинается с http:// или https://)
if (filePath.startsWith("http://") || filePath.startsWith("https://")) { if (filePath.startsWith("http://") || filePath.startsWith("https://")) {

View File

@ -55,7 +55,7 @@ export async function checkNetworkStatus(): Promise<boolean> {
const timeoutId = setTimeout(() => controller.abort(), 1500); const timeoutId = setTimeout(() => controller.abort(), 1500);
// Используем /auth/status так как он всегда доступен при наличии сети // Используем /auth/status так как он всегда доступен при наличии сети
const response = await fetch('/api/auth/status', { await fetch('/api/auth/status', {
method: 'GET', method: 'GET',
signal: controller.signal, signal: controller.signal,
cache: 'no-cache', cache: 'no-cache',