5.1.0: Fitbit: привязки к задачам, цели из API
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m23s

This commit is contained in:
poignatov
2026-02-09 17:06:08 +03:00
parent 29bd50acab
commit 242183a422
7 changed files with 724 additions and 349 deletions

View File

@@ -32,6 +32,22 @@ const FULL_STATISTICS_API_URL = '/d2dc349a-0d13-49b2-a8f0-1ab094bfba9b'
const mainTabs = ['current', 'tasks', 'wishlist', 'profile']
const deepTabs = ['add-words', 'test', 'task-form', 'wishlist-form', 'wishlist-detail', 'board-form', 'board-join', 'words', 'dictionaries', 'todoist-integration', 'telegram-integration', 'fitbit-integration', 'full', 'priorities', 'tracking', 'tracking-access', 'tracking-invite']
/**
* Гарантирует базовую запись истории для главного экрана перед глубоким табом.
* После долгого бездействия PWA может перезапуститься с одной записью в истории;
* кнопка "назад" тогда закрывает приложение. Эта функция добавляет запись для
* экрана 'current', чтобы "назад" возвращала на главный экран.
*/
function ensureBaseHistory(deepTab, params = {}, url) {
if (typeof window === 'undefined' || !deepTabs.includes(deepTab)) return
if (window.history.length <= 1) {
window.history.replaceState({ tab: 'current' }, '', '/')
window.history.pushState({ tab: deepTab, params, previousTab: 'current' }, '', url)
} else {
window.history.replaceState({ tab: deepTab, params, previousTab: 'current' }, '', url)
}
}
function AppContent() {
const { authFetch, isAuthenticated, loading: authLoading } = useAuth()
const prevIsAuthenticatedRef = useRef(null)
@@ -179,12 +195,12 @@ function AppContent() {
if (path.startsWith('/invite/')) {
const token = path.replace('/invite/', '')
if (token) {
const url = '/?tab=board-join&inviteToken=' + token
ensureBaseHistory('board-join', { inviteToken: token }, url)
setActiveTab('board-join')
setLoadedTabs(prev => ({ ...prev, 'board-join': true }))
setTabParams({ inviteToken: token })
setIsInitialized(true)
// Очищаем путь, оставляем только параметры
window.history.replaceState({}, '', '/?tab=board-join&inviteToken=' + token)
return
}
}
@@ -193,11 +209,12 @@ function AppContent() {
if (path.startsWith('/tracking/invite/')) {
const token = path.replace('/tracking/invite/', '')
if (token) {
const url = '/?tab=tracking-invite&inviteToken=' + token
ensureBaseHistory('tracking-invite', { inviteToken: token }, url)
setActiveTab('tracking-invite')
setLoadedTabs(prev => ({ ...prev, 'tracking-invite': true }))
setTabParams({ inviteToken: token })
setIsInitialized(true)
window.history.replaceState({}, '', '/?tab=tracking-invite&inviteToken=' + token)
return
}
}
@@ -206,16 +223,18 @@ function AppContent() {
const urlParams = new URLSearchParams(window.location.search)
const integration = urlParams.get('integration')
if (integration === 'fitbit') {
setActiveTab('fitbit-integration')
setLoadedTabs(prev => ({ ...prev, 'fitbit-integration': true }))
setIsInitialized(true)
// Перезаписываем URL с tab параметром и сохраняем integration/status для компонента
const status = urlParams.get('status')
const message = urlParams.get('message')
let newUrl = '/?tab=fitbit-integration&integration=fitbit'
if (status) newUrl += `&status=${status}`
if (message) newUrl += `&message=${message}`
window.history.replaceState({}, '', newUrl)
const fitbitParams = { integration: 'fitbit' }
if (status) fitbitParams.status = status
if (message) fitbitParams.message = message
ensureBaseHistory('fitbit-integration', fitbitParams, newUrl)
setActiveTab('fitbit-integration')
setLoadedTabs(prev => ({ ...prev, 'fitbit-integration': true }))
setIsInitialized(true)
return
}
@@ -224,10 +243,6 @@ function AppContent() {
const validTabs = ['current', 'priorities', 'full', 'words', 'add-words', 'dictionaries', 'test', 'tasks', 'task-form', 'wishlist', 'wishlist-form', 'wishlist-detail', 'board-form', 'board-join', 'profile', 'todoist-integration', 'telegram-integration', 'fitbit-integration', 'tracking', 'tracking-access', 'tracking-invite']
if (tabFromUrl && validTabs.includes(tabFromUrl) && deepTabs.includes(tabFromUrl)) {
// Если в URL есть глубокий таб, восстанавливаем его
setActiveTab(tabFromUrl)
setLoadedTabs(prev => ({ ...prev, [tabFromUrl]: true }))
// Восстанавливаем параметры из URL
const params = {}
urlParams.forEach((value, key) => {
@@ -239,6 +254,11 @@ function AppContent() {
}
}
})
const deepTabUrl = window.location.pathname + window.location.search
ensureBaseHistory(tabFromUrl, params, deepTabUrl)
// Если в URL есть глубокий таб, восстанавливаем его
setActiveTab(tabFromUrl)
setLoadedTabs(prev => ({ ...prev, [tabFromUrl]: true }))
if (Object.keys(params).length > 0) {
setTabParams(params)
// Если это экран full с selectedProject, восстанавливаем его