Добавлена поддержка колонки floating_toolbar_enabled в таблицу пользователей и обновлены соответствующие компоненты для управления отображением плавающей панели инструментов. Реализована логика настройки и сохранения этого параметра в профиле пользователя. Обновлены API и интерфейсы для поддержки новых функций.
This commit is contained in:
parent
e49b9dc865
commit
80c42f8df0
@ -502,6 +502,28 @@ function runMigrations() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем существование колонки floating_toolbar_enabled
|
||||||
|
const hasFloatingToolbarEnabled = columns.some(
|
||||||
|
(col) => col.name === "floating_toolbar_enabled"
|
||||||
|
);
|
||||||
|
if (!hasFloatingToolbarEnabled) {
|
||||||
|
db.run(
|
||||||
|
"ALTER TABLE users ADD COLUMN floating_toolbar_enabled INTEGER DEFAULT 1",
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(
|
||||||
|
"Ошибка добавления колонки floating_toolbar_enabled:",
|
||||||
|
err.message
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"Колонка floating_toolbar_enabled добавлена в таблицу users"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Проверяем существование колонки ai_enabled
|
// Проверяем существование колонки ai_enabled
|
||||||
const hasAiEnabled = columns.some((col) => col.name === "ai_enabled");
|
const hasAiEnabled = columns.some((col) => col.name === "ai_enabled");
|
||||||
if (!hasAiEnabled) {
|
if (!hasAiEnabled) {
|
||||||
@ -771,7 +793,7 @@ app.get("/api/user", requireApiAuth, (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sql =
|
const sql =
|
||||||
"SELECT username, email, avatar, accent_color, show_edit_date, colored_icons FROM users WHERE id = ?";
|
"SELECT username, email, avatar, accent_color, show_edit_date, colored_icons, floating_toolbar_enabled FROM users WHERE id = ?";
|
||||||
db.get(sql, [req.session.userId], (err, user) => {
|
db.get(sql, [req.session.userId], (err, user) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error("Ошибка получения данных пользователя:", err.message);
|
console.error("Ошибка получения данных пользователя:", err.message);
|
||||||
@ -1600,6 +1622,7 @@ app.put("/api/user/profile", requireApiAuth, async (req, res) => {
|
|||||||
accent_color,
|
accent_color,
|
||||||
show_edit_date,
|
show_edit_date,
|
||||||
colored_icons,
|
colored_icons,
|
||||||
|
floating_toolbar_enabled,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
const userId = req.session.userId;
|
const userId = req.session.userId;
|
||||||
|
|
||||||
@ -1675,6 +1698,11 @@ app.put("/api/user/profile", requireApiAuth, async (req, res) => {
|
|||||||
params.push(colored_icons ? 1 : 0);
|
params.push(colored_icons ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (floating_toolbar_enabled !== undefined) {
|
||||||
|
updateFields.push("floating_toolbar_enabled = ?");
|
||||||
|
params.push(floating_toolbar_enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (newPassword) {
|
if (newPassword) {
|
||||||
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
||||||
updateFields.push("password = ?");
|
updateFields.push("password = ?");
|
||||||
|
|||||||
@ -82,7 +82,7 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict';
|
|||||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
}, {
|
}, {
|
||||||
"url": "index.html",
|
"url": "index.html",
|
||||||
"revision": "0.b944c6vblpo"
|
"revision": "0.2vg2p27g3bg"
|
||||||
}], {});
|
}], {});
|
||||||
workbox.cleanupOutdatedCaches();
|
workbox.cleanupOutdatedCaches();
|
||||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export const userApi = {
|
|||||||
accent_color?: string;
|
accent_color?: string;
|
||||||
show_edit_date?: boolean;
|
show_edit_date?: boolean;
|
||||||
colored_icons?: boolean;
|
colored_icons?: boolean;
|
||||||
|
floating_toolbar_enabled?: boolean;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const { data } = await axiosClient.put("/user/profile", profile);
|
const { data } = await axiosClient.put("/user/profile", profile);
|
||||||
|
|||||||
@ -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,13 @@ 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 user = useAppSelector((state) => state.profile.user);
|
||||||
|
|
||||||
|
// Проверяем, включена ли плавающая панель
|
||||||
|
const floatingToolbarEnabled =
|
||||||
|
user?.floating_toolbar_enabled !== undefined
|
||||||
|
? user.floating_toolbar_enabled === 1
|
||||||
|
: true;
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (!content.trim()) {
|
if (!content.trim()) {
|
||||||
@ -684,7 +690,7 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
|||||||
|
|
||||||
// Обработчик выделения текста
|
// Обработчик выделения текста
|
||||||
const handleSelection = useCallback(() => {
|
const handleSelection = useCallback(() => {
|
||||||
if (isPreviewMode) {
|
if (isPreviewMode || !floatingToolbarEnabled) {
|
||||||
setShowFloatingToolbar(false);
|
setShowFloatingToolbar(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -709,7 +715,13 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
|||||||
setHasSelection(false);
|
setHasSelection(false);
|
||||||
setActiveFormats({ bold: false, italic: false, strikethrough: false });
|
setActiveFormats({ bold: false, italic: false, strikethrough: false });
|
||||||
}
|
}
|
||||||
}, [isPreviewMode, content, getCursorPosition, getActiveFormats]);
|
}, [
|
||||||
|
isPreviewMode,
|
||||||
|
content,
|
||||||
|
getCursorPosition,
|
||||||
|
getActiveFormats,
|
||||||
|
floatingToolbarEnabled,
|
||||||
|
]);
|
||||||
|
|
||||||
// Отслеживание выделения текста
|
// Отслеживание выделения текста
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -955,16 +967,18 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FloatingToolbar
|
{floatingToolbarEnabled && (
|
||||||
textareaRef={textareaRef}
|
<FloatingToolbar
|
||||||
onFormat={insertMarkdown}
|
textareaRef={textareaRef}
|
||||||
visible={showFloatingToolbar}
|
onFormat={insertMarkdown}
|
||||||
position={toolbarPosition}
|
visible={showFloatingToolbar}
|
||||||
onHide={() => setShowFloatingToolbar(false)}
|
position={toolbarPosition}
|
||||||
onInsertColor={insertColorMarkdown}
|
onHide={() => setShowFloatingToolbar(false)}
|
||||||
activeFormats={activeFormats}
|
onInsertColor={insertColorMarkdown}
|
||||||
hasSelection={hasSelection}
|
activeFormats={activeFormats}
|
||||||
/>
|
hasSelection={hasSelection}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -67,6 +67,12 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
|||||||
const { showNotification } = useNotification();
|
const { showNotification } = useNotification();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
useMarkdown({ onNoteUpdate: onReload }); // Инициализируем обработчики спойлеров, внешних ссылок и чекбоксов
|
useMarkdown({ onNoteUpdate: onReload }); // Инициализируем обработчики спойлеров, внешних ссылок и чекбоксов
|
||||||
|
|
||||||
|
// Проверяем, включена ли плавающая панель
|
||||||
|
const floatingToolbarEnabled =
|
||||||
|
user?.floating_toolbar_enabled !== undefined
|
||||||
|
? user.floating_toolbar_enabled === 1
|
||||||
|
: true;
|
||||||
|
|
||||||
const handleEdit = () => {
|
const handleEdit = () => {
|
||||||
setIsEditing(true);
|
setIsEditing(true);
|
||||||
@ -594,7 +600,7 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
|||||||
|
|
||||||
// Обработчик выделения текста
|
// Обработчик выделения текста
|
||||||
const handleSelection = useCallback(() => {
|
const handleSelection = useCallback(() => {
|
||||||
if (localPreviewMode) {
|
if (localPreviewMode || !floatingToolbarEnabled) {
|
||||||
setShowFloatingToolbar(false);
|
setShowFloatingToolbar(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -619,7 +625,7 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
|||||||
setHasSelection(false);
|
setHasSelection(false);
|
||||||
setActiveFormats({ bold: false, italic: false, strikethrough: false });
|
setActiveFormats({ bold: false, italic: false, strikethrough: false });
|
||||||
}
|
}
|
||||||
}, [localPreviewMode, editContent, getCursorPosition, getActiveFormats]);
|
}, [localPreviewMode, editContent, getCursorPosition, getActiveFormats, floatingToolbarEnabled]);
|
||||||
|
|
||||||
const handleImageButtonClick = () => {
|
const handleImageButtonClick = () => {
|
||||||
imageInputRef.current?.click();
|
imageInputRef.current?.click();
|
||||||
@ -1221,16 +1227,18 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FloatingToolbar
|
{floatingToolbarEnabled && (
|
||||||
textareaRef={editTextareaRef}
|
<FloatingToolbar
|
||||||
onFormat={insertMarkdown}
|
textareaRef={editTextareaRef}
|
||||||
visible={showFloatingToolbar}
|
onFormat={insertMarkdown}
|
||||||
position={toolbarPosition}
|
visible={showFloatingToolbar}
|
||||||
onHide={() => setShowFloatingToolbar(false)}
|
position={toolbarPosition}
|
||||||
onInsertColor={insertColorMarkdown}
|
onHide={() => setShowFloatingToolbar(false)}
|
||||||
activeFormats={activeFormats}
|
onInsertColor={insertColorMarkdown}
|
||||||
hasSelection={hasSelection}
|
activeFormats={activeFormats}
|
||||||
/>
|
hasSelection={hasSelection}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ const SettingsPage: React.FC = () => {
|
|||||||
const [selectedAccentColor, setSelectedAccentColor] = useState("#007bff");
|
const [selectedAccentColor, setSelectedAccentColor] = useState("#007bff");
|
||||||
const [showEditDate, setShowEditDate] = useState(true);
|
const [showEditDate, setShowEditDate] = useState(true);
|
||||||
const [coloredIcons, setColoredIcons] = useState(true);
|
const [coloredIcons, setColoredIcons] = useState(true);
|
||||||
|
const [floatingToolbarEnabled, setFloatingToolbarEnabled] = useState(true);
|
||||||
|
|
||||||
// AI settings
|
// AI settings
|
||||||
const [apiKey, setApiKey] = useState("");
|
const [apiKey, setApiKey] = useState("");
|
||||||
@ -134,6 +135,11 @@ const SettingsPage: React.FC = () => {
|
|||||||
: true;
|
: true;
|
||||||
setColoredIcons(coloredIconsValue);
|
setColoredIcons(coloredIconsValue);
|
||||||
updateColoredIconsClass(coloredIconsValue);
|
updateColoredIconsClass(coloredIconsValue);
|
||||||
|
const floatingToolbarValue =
|
||||||
|
userData.floating_toolbar_enabled !== undefined
|
||||||
|
? userData.floating_toolbar_enabled === 1
|
||||||
|
: true;
|
||||||
|
setFloatingToolbarEnabled(floatingToolbarValue);
|
||||||
|
|
||||||
// Загружаем AI настройки
|
// Загружаем AI настройки
|
||||||
try {
|
try {
|
||||||
@ -166,6 +172,7 @@ const SettingsPage: React.FC = () => {
|
|||||||
accent_color: selectedAccentColor,
|
accent_color: selectedAccentColor,
|
||||||
show_edit_date: showEditDate,
|
show_edit_date: showEditDate,
|
||||||
colored_icons: coloredIcons,
|
colored_icons: coloredIcons,
|
||||||
|
floating_toolbar_enabled: floatingToolbarEnabled,
|
||||||
});
|
});
|
||||||
dispatch(setAccentColorAction(selectedAccentColor));
|
dispatch(setAccentColorAction(selectedAccentColor));
|
||||||
setAccentColor(selectedAccentColor);
|
setAccentColor(selectedAccentColor);
|
||||||
@ -672,6 +679,31 @@ const SettingsPage: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group ai-toggle-group">
|
||||||
|
<label className="ai-toggle-label">
|
||||||
|
<div className="toggle-label-content">
|
||||||
|
<span className="toggle-text-main">
|
||||||
|
Плавающая панель редактирования
|
||||||
|
</span>
|
||||||
|
<span className="toggle-text-desc">
|
||||||
|
{floatingToolbarEnabled
|
||||||
|
? "Показывать плавающую панель инструментов при выделении текста в редакторе"
|
||||||
|
: "Скрывать плавающую панель инструментов при выделении текста"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="toggle-switch-wrapper">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="floating-toolbar-toggle"
|
||||||
|
className="toggle-checkbox"
|
||||||
|
checked={floatingToolbarEnabled}
|
||||||
|
onChange={(e) => setFloatingToolbarEnabled(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span className="toggle-slider"></span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button className="btnSave" onClick={handleUpdateAppearance}>
|
<button className="btnSave" onClick={handleUpdateAppearance}>
|
||||||
Сохранить изменения
|
Сохранить изменения
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ export interface User {
|
|||||||
accent_color: string;
|
accent_color: string;
|
||||||
show_edit_date?: number;
|
show_edit_date?: number;
|
||||||
colored_icons?: number;
|
colored_icons?: number;
|
||||||
|
floating_toolbar_enabled?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthResponse {
|
export interface AuthResponse {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user