Добавлена поддержка колонки 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
|
||||
const hasAiEnabled = columns.some((col) => col.name === "ai_enabled");
|
||||
if (!hasAiEnabled) {
|
||||
@ -771,7 +793,7 @@ app.get("/api/user", requireApiAuth, (req, res) => {
|
||||
}
|
||||
|
||||
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) => {
|
||||
if (err) {
|
||||
console.error("Ошибка получения данных пользователя:", err.message);
|
||||
@ -1600,6 +1622,7 @@ app.put("/api/user/profile", requireApiAuth, async (req, res) => {
|
||||
accent_color,
|
||||
show_edit_date,
|
||||
colored_icons,
|
||||
floating_toolbar_enabled,
|
||||
} = req.body;
|
||||
const userId = req.session.userId;
|
||||
|
||||
@ -1675,6 +1698,11 @@ app.put("/api/user/profile", requireApiAuth, async (req, res) => {
|
||||
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) {
|
||||
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
||||
updateFields.push("password = ?");
|
||||
|
||||
@ -82,7 +82,7 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict';
|
||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||
}, {
|
||||
"url": "index.html",
|
||||
"revision": "0.b944c6vblpo"
|
||||
"revision": "0.2vg2p27g3bg"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
|
||||
@ -14,6 +14,7 @@ export const userApi = {
|
||||
accent_color?: string;
|
||||
show_edit_date?: boolean;
|
||||
colored_icons?: boolean;
|
||||
floating_toolbar_enabled?: boolean;
|
||||
}
|
||||
) => {
|
||||
const { data } = await axiosClient.put("/user/profile", profile);
|
||||
|
||||
@ -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,13 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
||||
const isPreviewMode = useAppSelector((state) => state.ui.isPreviewMode);
|
||||
const { showNotification } = useNotification();
|
||||
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 () => {
|
||||
if (!content.trim()) {
|
||||
@ -684,7 +690,7 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
||||
|
||||
// Обработчик выделения текста
|
||||
const handleSelection = useCallback(() => {
|
||||
if (isPreviewMode) {
|
||||
if (isPreviewMode || !floatingToolbarEnabled) {
|
||||
setShowFloatingToolbar(false);
|
||||
return;
|
||||
}
|
||||
@ -709,7 +715,13 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
||||
setHasSelection(false);
|
||||
setActiveFormats({ bold: false, italic: false, strikethrough: false });
|
||||
}
|
||||
}, [isPreviewMode, content, getCursorPosition, getActiveFormats]);
|
||||
}, [
|
||||
isPreviewMode,
|
||||
content,
|
||||
getCursorPosition,
|
||||
getActiveFormats,
|
||||
floatingToolbarEnabled,
|
||||
]);
|
||||
|
||||
// Отслеживание выделения текста
|
||||
useEffect(() => {
|
||||
@ -955,6 +967,7 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{floatingToolbarEnabled && (
|
||||
<FloatingToolbar
|
||||
textareaRef={textareaRef}
|
||||
onFormat={insertMarkdown}
|
||||
@ -965,6 +978,7 @@ export const NoteEditor: React.FC<NoteEditorProps> = ({ onSave }) => {
|
||||
activeFormats={activeFormats}
|
||||
hasSelection={hasSelection}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@ -68,6 +68,12 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
||||
const dispatch = useAppDispatch();
|
||||
useMarkdown({ onNoteUpdate: onReload }); // Инициализируем обработчики спойлеров, внешних ссылок и чекбоксов
|
||||
|
||||
// Проверяем, включена ли плавающая панель
|
||||
const floatingToolbarEnabled =
|
||||
user?.floating_toolbar_enabled !== undefined
|
||||
? user.floating_toolbar_enabled === 1
|
||||
: true;
|
||||
|
||||
const handleEdit = () => {
|
||||
setIsEditing(true);
|
||||
setEditContent(note.content);
|
||||
@ -594,7 +600,7 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
||||
|
||||
// Обработчик выделения текста
|
||||
const handleSelection = useCallback(() => {
|
||||
if (localPreviewMode) {
|
||||
if (localPreviewMode || !floatingToolbarEnabled) {
|
||||
setShowFloatingToolbar(false);
|
||||
return;
|
||||
}
|
||||
@ -619,7 +625,7 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
||||
setHasSelection(false);
|
||||
setActiveFormats({ bold: false, italic: false, strikethrough: false });
|
||||
}
|
||||
}, [localPreviewMode, editContent, getCursorPosition, getActiveFormats]);
|
||||
}, [localPreviewMode, editContent, getCursorPosition, getActiveFormats, floatingToolbarEnabled]);
|
||||
|
||||
const handleImageButtonClick = () => {
|
||||
imageInputRef.current?.click();
|
||||
@ -1221,6 +1227,7 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{floatingToolbarEnabled && (
|
||||
<FloatingToolbar
|
||||
textareaRef={editTextareaRef}
|
||||
onFormat={insertMarkdown}
|
||||
@ -1231,6 +1238,7 @@ export const NoteItem: React.FC<NoteItemProps> = ({
|
||||
activeFormats={activeFormats}
|
||||
hasSelection={hasSelection}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ const SettingsPage: React.FC = () => {
|
||||
const [selectedAccentColor, setSelectedAccentColor] = useState("#007bff");
|
||||
const [showEditDate, setShowEditDate] = useState(true);
|
||||
const [coloredIcons, setColoredIcons] = useState(true);
|
||||
const [floatingToolbarEnabled, setFloatingToolbarEnabled] = useState(true);
|
||||
|
||||
// AI settings
|
||||
const [apiKey, setApiKey] = useState("");
|
||||
@ -134,6 +135,11 @@ const SettingsPage: React.FC = () => {
|
||||
: true;
|
||||
setColoredIcons(coloredIconsValue);
|
||||
updateColoredIconsClass(coloredIconsValue);
|
||||
const floatingToolbarValue =
|
||||
userData.floating_toolbar_enabled !== undefined
|
||||
? userData.floating_toolbar_enabled === 1
|
||||
: true;
|
||||
setFloatingToolbarEnabled(floatingToolbarValue);
|
||||
|
||||
// Загружаем AI настройки
|
||||
try {
|
||||
@ -166,6 +172,7 @@ const SettingsPage: React.FC = () => {
|
||||
accent_color: selectedAccentColor,
|
||||
show_edit_date: showEditDate,
|
||||
colored_icons: coloredIcons,
|
||||
floating_toolbar_enabled: floatingToolbarEnabled,
|
||||
});
|
||||
dispatch(setAccentColorAction(selectedAccentColor));
|
||||
setAccentColor(selectedAccentColor);
|
||||
@ -672,6 +679,31 @@ const SettingsPage: React.FC = () => {
|
||||
</label>
|
||||
</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>
|
||||
|
||||
@ -5,6 +5,7 @@ export interface User {
|
||||
accent_color: string;
|
||||
show_edit_date?: number;
|
||||
colored_icons?: number;
|
||||
floating_toolbar_enabled?: number;
|
||||
}
|
||||
|
||||
export interface AuthResponse {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user