4.21.0: Исправление навигации и истории
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m15s

This commit is contained in:
poignatov
2026-02-05 13:52:13 +03:00
parent 22f6807eb2
commit 9a066c88ac
15 changed files with 67 additions and 25 deletions

View File

@@ -1 +1 @@
4.20.7
4.21.0

View File

@@ -1,6 +1,6 @@
{
"name": "play-life-web",
"version": "4.20.7",
"version": "4.21.0",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -750,8 +750,33 @@ function AppContent() {
setTabParams(params)
// Обновляем URL только для глубоких табов
if (isNewTabDeep) {
// Сохраняем текущий таб как предыдущий при переходе на глубокий таб
updateUrl(tab, params, activeTab)
// Проверяем, была ли последняя запись в истории от модального окна
const currentState = window.history.state || {}
const isFromModal = currentState.modalOpen === true
const isNavigatingToForm = tab === 'task-form' || tab === 'wishlist-form'
if (isFromModal && isNavigatingToForm) {
// Заменяем запись модального окна на запись формы редактирования
// Используем replaceState вместо pushState, сохраняя activeTab как previousTab
const url = new URL(window.location)
url.searchParams.set('tab', tab)
// Удаляем старые параметры
const keysToRemove = []
url.searchParams.forEach((value, key) => {
if (key !== 'tab') keysToRemove.push(key)
})
keysToRemove.forEach(key => url.searchParams.delete(key))
// Добавляем новые параметры
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.set(key, typeof value === 'object' ? JSON.stringify(value) : value)
}
})
window.history.replaceState({ tab, params, previousTab: activeTab }, '', url)
} else {
// Сохраняем текущий таб как предыдущий при переходе на глубокий таб
updateUrl(tab, params, activeTab)
}
} else if (isNewTabMain && isCurrentTabDeep) {
// При переходе с глубокого таба на основной - очищаем URL и сохраняем таб в state
clearUrl(tab)

View File

@@ -139,7 +139,7 @@ function AddWords({ onNavigate, dictionaryId, dictionaryName }) {
}
const handleClose = () => {
onNavigate?.('words', dictionaryId !== undefined && dictionaryId !== null ? { dictionaryId } : {})
window.history.back()
}
// Show loading state while fetching dictionary name

View File

@@ -156,7 +156,7 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
}
const handleClose = () => {
onNavigate('wishlist')
window.history.back()
}
if (loadingBoard) {

View File

@@ -94,7 +94,7 @@ function DictionaryList({ onNavigate, refreshTrigger = 0 }) {
{/* Кнопка закрытия */}
<button
className="dictionary-close-button"
onClick={() => onNavigate?.('profile')}
onClick={() => window.history.back()}
title="Закрыть"
>

View File

@@ -127,7 +127,7 @@ function FullStatistics({ selectedProject, onClearSelection, data, loading, erro
onClick={() => {
// Сбрасываем выбор дня перед выходом с экрана
setSelectedDate(todayDateStr)
onNavigate('current')
window.history.back()
}}
className="close-x-button"
title="Закрыть"

View File

@@ -921,7 +921,7 @@ function ProjectPriorityManager({ allProjectsData, currentWeekData, shouldLoad,
<div className="max-w-2xl mx-auto flex flex-col h-full">
{onNavigate && (
<button
onClick={() => onNavigate('current')}
onClick={() => window.history.back()}
className="close-x-button"
title="Закрыть"
>

View File

@@ -735,7 +735,9 @@ function TaskDetail({ taskId, onClose, onRefresh, onTaskCompleted, onNavigate })
<h2
className="task-detail-title"
onClick={taskDetail ? () => {
onClose?.()
// Закрываем модальное окно БЕЗ history.back() (skipHistoryBack = true)
// handleTabChange заменит запись модального окна через replaceState
onClose?.(true)
onNavigate?.('task-form', { taskId: taskId })
} : undefined}
style={{ cursor: taskDetail ? 'pointer' : 'default' }}

View File

@@ -774,7 +774,7 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
const handleCancel = () => {
resetForm()
onNavigate?.('tasks')
window.history.back()
}
const handleDelete = async () => {

View File

@@ -71,8 +71,15 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, error, onRetry
setSelectedTaskForDetail(task.id)
}
const handleCloseDetail = () => {
setSelectedTaskForDetail(null)
const handleCloseDetail = (skipHistoryBack = false) => {
// Если skipHistoryBack = true (например, при навигации на форму редактирования),
// просто закрываем модальное окно без history.back()
if (!skipHistoryBack && historyPushedForDetailRef.current) {
window.history.back()
} else {
historyPushedForDetailRef.current = false
setSelectedTaskForDetail(null)
}
}
// Добавляем запись в историю при открытии модальных окон и обрабатываем "назад"

View File

@@ -557,7 +557,7 @@ function TestWords({ onNavigate, wordCount: initialWordCount, configId: initialC
}
const handleClose = () => {
onNavigate?.('tasks')
window.history.back()
}
const handleStartTest = () => {

View File

@@ -52,6 +52,9 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, p
}, [wishlistId, fetchWishlistDetail])
const handleEdit = () => {
// Сбрасываем флаг, чтобы handleClose не вызвал history.back()
// handleTabChange заменит запись модального окна через replaceState
historyPushedForWishlistRef.current = false
onClose?.()
onNavigate?.('wishlist-form', { wishlistId: wishlistId, boardId: boardId })
}
@@ -192,8 +195,15 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, p
}
}
const handleCloseDetail = () => {
setSelectedTaskForDetail(null)
const handleCloseDetail = (skipHistoryBack = false) => {
// Если skipHistoryBack = true (например, при навигации на форму редактирования),
// просто закрываем модальное окно без history.back()
if (!skipHistoryBack && historyPushedForTaskRef.current) {
window.history.back()
} else {
historyPushedForTaskRef.current = false
setSelectedTaskForDetail(null)
}
}
// Добавляем запись в историю при открытии модальных окон и обрабатываем "назад"
@@ -278,8 +288,11 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, p
}, [wishlistId, selectedTaskForDetail, onClose, onNavigate, previousTab, boardId])
const handleClose = () => {
// Используем onClose если передан, иначе возвращаемся на wishlist
if (onClose) {
// Если была добавлена запись в историю, удаляем её через history.back()
// Обработчик popstate закроет модальное окно
if (historyPushedForWishlistRef.current) {
window.history.back()
} else if (onClose) {
onClose()
} else {
// Возвращаемся на предыдущий таб, если он был сохранен, иначе на wishlist

View File

@@ -666,12 +666,7 @@ function WishlistForm({ onNavigate, wishlistId, editConditionIndex, newTaskId, b
const handleCancel = () => {
resetForm()
// Возвращаемся на доску, если она была указана
if (boardId) {
onNavigate?.('wishlist', { boardId })
} else {
onNavigate?.('wishlist')
}
window.history.back()
}
return (

View File

@@ -176,7 +176,7 @@ function WordList({ onNavigate, dictionaryId, isNewDictionary, refreshTrigger =
return (
<div className="word-list">
<button
onClick={() => onNavigate?.('dictionaries')}
onClick={() => window.history.back()}
className="close-x-button"
title="Закрыть"
>